Setting Up For Today

Welcome the Data Visualization Workshop! Data Viz plays a really crucial role in the more general data analysis workflow:

Adequately teaching how to do data summarization would be a whole entirely different workshop. You’ll still be able to work along here, but if you’re unfamiliar with R and the tidyverse, you might have to resort to just copy-pasting code as we go along.

But, for materials for learning more about the basics of R, I’d refer you to my 2017 LSA couse materials:

  1. Intro to R
  2. Data and DataFrames
  3. Split-Apply-Combine

The process of learning R

These are some of the core areas I figure are necessary to getting good at statistical modelling in R:

  1. Using R (and RStudio) well
  2. Feeling comfortable and fluid reorganizing and summarizing data
  3. Visualizing Data
  4. Deciding before you model what you want to compare to what
  5. How to translate your analysis goals into R code
  6. Understanding a little bit about statistics
  7. When something goes wrong, being able to accurately attribute your difficulty to one of the above topics

These are all skills you can achieve through practice, experience, and occasional guidance from someone more skilled than you. It is exactly like acquiring any other skill or craft. At first it will be confusing, you’ll make some mistakes, and it won’t look so good. I like comparing it to knitting.

The first hat I ever knit:

A more recent hat I knit:

The way I improved my knitting is exactly the same as how you can improve your R programming ability:

  • I knit a lot (almost every day).
  • I memorized a bunch of stuff.
  • Remembered where to look up the stuff I don’t have memorized.
  • My knitting became more “idiomatic” (i.e. I started knitting like how other knitters knit).
  • I learned how to identify and fix mistakes without undoing my entire project.
  • I developed good workspace hygiene & organization.
  • As I got the basics down, I started researching and incorporating fussy little details into my work.

R, RStudio and R Notebooks

We’re going to be using R, RStudio, and R Notebooks in this course, and it’s a little important to keep straight what these three things are:

R

R is a programming language that runs on your computer. At its barest bones, it looks like this:

You can type text into the prompt there, and if you’ve successfully memorized the right R commands, it’ll do some things.

RStudio

RStudio is like an Instagram filter over to of R, to make your R use experience better. It visually organizes some important components of using R into panes, and offers code completion suggestions. For example, if you ember there’s something called a “Wilcoxon test”, but you don’t remember what the function in R is, you can start typing in Wilc, and this will happen:

RStudio’s autocompletion is really useful for a lot of other things, like reminding you what the column names are in your data frame, what the names of all the arguments to a function are, etc.

But perhaps the most valuable component in R Studio these days is its authoring tools, like R Notebooks

R Notebooks

R Notebooks allow you to document your code in plain text, insert R Code chunks, and view the results of the R code all in one place, then compile it into a nice looking notebook.

Discussion

I’m going to recommend (for now at least) that you run all of your code though an R Notebook. It is possible to just type things into the R console, but that’s kind of like dictating a paper into thin air. Once you’ve spoken the words, they disappear and can be hard to recover.

My earlier advice would have been to write all of your code in an R script file, but that also separates the code from its results, which can be hard for beginners to keep track of.


Installing R Packages

R comes with a lot of functionality installed, but one way that R is extensible is through users’ ability to contribute new code & data through it’s package management system. We’re going to using a number of these packages in the course, especially since a few of them have fundamentally changed the way R programming works in the past 3 years. There’s also a course R package I’ve created to easily distribute sample datasets.

Here’s a basic diagram of how R packages work:

Installing Packages

install.packages()

Most R packages are distributed through CRAN (Comprehensive R Archive Network). When you run function install.packages("x"), R checks whether the package "x" exists on CRAN, and installs it on your computer if it does. You maybe asked to choose a “CRAN mirror” the first time you run install.packages(). This is because there are many copies of CRAN distributed across the internet. I’d recommend choosing the first option called 0-Cloud.

install_github()

As a package developer, getting a package onto CRAN can be a bit of a pain, so some packages (and development versions of many) are also available on GitHub, which can be easily installed with devtools::install_github("username/package").

Installing packages is different from loading packages

Installing a package is different from loading packages. Installing a package only downloads and configures the code on your computer. In order to use the contents of a package, you need to load it into your R session with library().

  • You only need to run install.packages() once to install a package, or to update a package.
  • You need to run library() at the start of every new R session in order to use the functionality from that package.

For example, ggplot() is a function from the package ggplot2. I have already installed ggplot2 on my computer, but if I try to use ggplot() before loading the package with library(), I’ll get the error that the function was not found.

foo <- ggplot()
Error in ggplot() : could not find function "ggplot"
library("ggplot2")
foo <- ggplot()

~2 Minute Activity Let’s actually load up the initial packages we’re going to use today:

library(tidyverse)
── Attaching packages ──────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.5
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ─────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(lsa2017)
library(broom)

First Principles of Plotting

Why Plot

Data visualization is an essential component of data analysis. In fact, I believe it is necessary to learn how to plot your data before you learn how to do statistical modelling. If you haven’t made a lot of graphs of your data, and have only looked at averages, correlations, and linear model results, that you don’t really understand your data.

There’s a classic illustration of this called Anscombe’s quartet, which when plotted looks like three very distinctive patterns.

But if you fit linear models to them, they have nearly identical statistical properties.

fit_lm <- function(df){
  lm(y ~ x, data = df)
}
anscomb_models <- tidy_anscombe %>%
                    group_by(series) %>%
                    nest() %>%
                    mutate(model = map(data, fit_lm),
                           model_param_df = map(model, tidy),
                           model_glance = map(model, glance))
anscomb_models %>%
  unnest(model_param_df) %>%
  arrange(term)
anscomb_models %>%
  unnest(model_glance)

It has been even more humorously illustrated recently that you can produce data sets of almost any arbitrary shape that have nearly identical statistical properties.

Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing

In fact, more and more people are starting to use statistical techniques where the only plausible way to report the results of the model is with a graphical display.

Thinking about Plotting

It’s important to think of your figures as a report of your data. Try to take as much care in producing your plots as you do your writing, or reporting of your statistics. They are as important as (or for some readers, more important than) anything else in your paper.

“Accuracy”

When making a plot, you should strive for accuracy in:

  • Accurately representing the properties of numbers.
  • Accurately representing the nature of your data.

Take this very simple data set:

group value
A 2
B 5

For our purposes, these numbers have three properties.

  1. Order: 2 < 5, or A < B
  2. Magnitude: 5 = 2.5 \(\times\) 2, or B = 2.5 \(\times\) A
  3. Contextual Magnitude: If A and B are bars, and these are measure of the cost of a pint, then A must be a real dive (and a good deal), and B must be a little bit better, but still not too fancy. If A and B are people, and these are their number of legs, then A has an unsurprising number of legs and B has a surprising number of legs.

Here is an example of an inaccurate plot:

It successfully captures the order of A and B, but fails to capture the correct magnitude of the difference. The magnitude of the difference is thrown off because the y-axis doesn’t start at 0. In this plot, the B line is 7\(\times\) longer than the A line, but the actual magnitude of the difference is 2.5\(\times\). This produces a “lie factor” of \(\frac{7}{2.5} = 2.8\).

This isn’t just a hypothetical problem either. For example, British electoral mailers are notorious for the inaccurately portraying the magnitude of differences.

Both academic researchers and the producers of these political mailers may counter by saying

But the axes were labelled accurately!

If readers would understand your data better if they ignored the graphical elements of your plot, then your plot is net-negative to accurate communication, and I can only assume that accuracy was never your primary goal anyway.

Think about how plots are read

The way in which we read the numbers off of plots also matters a lot to constructing a figure. For example, the bar plot above maps the number of MPs to the length of the bars. Another graphical feature we could use is to map the number of MPs to the position of points.

Or, we could map it to the area of circles:

Or, to the angle of a pie chart slice:

However, not all visual dimensions are as easilly decoded visially as others. Research has found that people are more accurate in their perception of numeric differences when graphs use length and position, rather than area, or angle.

Colo(u)r

We also need to think a lot about how we use color in figures. In the political graphs, there are clear and iconic colors associated with each political party, which I would recommend using in a case like that. Choosing a different color palette makes the graph more confusing.

There are many other kinds of conventionalized color-meaning mapping that you should usually stick to (like cold = blue, hot = red). But you should also be careful to avoid using conventionalized color schemes that either reenforce harmful stereotypes (like pinking and bluing gender) or could be otherwise offensive to cultural sensitivities.

You should also be careful to avoid accidentally conveying something you don’t intend to with your color choices:

There’s no sensible order to the voicing contexts that /ay/ appears in above, but one seems to be implied through the use of a gradient color scheme. The voiced context is also specially highlighted by being maped to a red hue while the rest are mapped to blue hues. Rather, we’d probably want a color palette like one of the two below, where the colors are distinct, unordered, and perceptually uniform.

ggplot2 basic concepts

Why learn ggplot2?

There are a few different graphics packages out there to use, including base R plots and lattice. ggplot2, however, is much more plugged into the tidyverse workflow, which seems to be the trending direction of R Programming. It’s also extensible, meaning people are producing a lot of really cool and really useful add ons!

Layers, Aesthetics, Geometries and Statistics

We’re going to start by working with the /ay/ dataset from the lsa2017 package.

The /ay/ dataset

This /ay/ data set contains over 80,000 tokens of the /ay/ vowel which were automatically extracted from 326 sociolinguistic interviews in Philadelphia. There are two allophones of /ay/ encoded in the data column plt_vclass

  • ay0 = pre-voiceless /ay/
  • ay = all other /ay/

Across the 20th century, the vowel quality of pre-voiceless /ay/ underwent a large change from something like [ɑɪ] to [ʌɪ]. This was a phonetically gradual change, so we’ll be plotting it according to the primary phonetic correlate of this change, normalized F1.

To kick things off, we’ll just estimate the average normalized F1

ay_means <- ay %>%
            mutate(dob = year-age)%>%
            group_by(idstring, dob, plt_vclass)%>%
            summarise(F1_n = mean(F1_n))
head(ay_means)

We’re going to take this data and build up to making this plot:

Layers

You should hopefully start looking at figures like this one like many of us look at the image below.

Those of use familiar with this kind of media know that the picture of the libarary is not what was originally capture by my phone. Rather there are multiple layers of effects, filters and text on top of the base image, which produce the final image. And in fact, some of these layers are crucially ordered. For example, the text would look different if it was added to the image first, and then the filters, instead of vice versa.

So too with the ggplot2 plot above. These plots are constructed out of layers. Every component of the graph, from the underlying data it’s plotting, to the coordinate system it’s plotted on, to the statistical summaries overlaid on top, to the axis labels, are layers in the plot. The consequence of this is that your use of ggplot2 will probably involve iterative addition of layer upon layer until you’re pleased with the results.

Aesthetics

The graphical properties which encode the data you’re presenting are the aesthetics of the plot. These include things like

  • x position
  • y position
  • size of elements
  • shape of elements
  • color of elements

Geometries

The primary visual items on the plots are called geometries and include things like

  • points
  • lines
  • line segments
  • bars
  • text

Some of these geometries have their own specific aesthetic settings. For example,

  • points
    • point shape
  • text
    • text labels
  • lines
    • line weight
    • line type

Statistics

You’ll also frequently want to plot statistics overlaid on top of, or instead of the raw data. Some of these include

  • Smoothing and regression lines
  • One and two dimensional binning
  • Mean and medians with confidence intervals.

The aesthetics, geometries and statistics constitute the most important layers of a plot, but for fine tuning a plot for publication, there are a number of other things you’ll want to adjust. The most common one of these are the scales, which encompass things like

  • A logarithmic x or y axis
  • Customized color scales
  • Customized point shapes, or linetypes

We’ll review many of these components as we build up the plot, and will circle back to more of them for greater detail.

Building the Plot

First, let’s refresh our memories of the graph we want to build.

This plot is composed of eight layers, which can be subdivided into five layer types. It’s not important for you to memorize these layer types, but it helps to structure the discussion.

Layers

The data layer

Every ggplot2 plot has a data layer, which defines the data set to plot, and the basic mappings of data to aesthetic elements. The data layer created with the functions ggplot() and aes(), and looks like this

ggplot(data, aes(...))

The first argument to ggplot() is a data frame (it must be a data frame), and its second argument is aes(). You’re never going to use aes() in any other context except for inside of other ggplot2 functions, so it might be best not to think of aes() as its own function, but rather as a special way of defining data-to-aesthetic mappings.

Also as a reminder, we’ll be working with a dataframe that looks like this:

head(ay_means)

We’ll start by mapping the dob to the x-axis, and F1_n to the y-axis.

p <- ggplot(ay_means, aes(x = dob, y = F1_n))
p

You can think of this plot as the base image, before we’ve added any extra layers, text or instagram filters to it. An important conceptual issue is that you are able to assign plots to variables (in this case, p). When you do this assignment, nothing special happens. But if you print out p, R will generate the plot.

The geometries layer

The next step, after defining the basic data-to-aesthetic mappings, is to add geometries to the data. We’ll discuss geometries in more detail below, but for now, we’ll add one of the simplest: points.

  p <- p + geom_point()
  p

There are a few things to take away from this step. First and foremost, the way you add new layers, of any kind, to a plot is with the + operator. And, as we’ll see in a moment, there’s no need to only add them one at a time. You can string together any number of layers to add to a plot, separated by +.

The next thing to notice is that all layers you add to a plot are, technically, functions. We didn’t pass any arguments to geom_point(), so the resulting plot represents the default behavior: solid black circular points.

If for no good reason at all we wanted to use a different point shape in the plot, we could specify it inside of geom_point().

ggplot(ay_means, aes(x=dob, y=F1_n)) +
  geom_point(shape = 3)

Or, if we wanted to use larger, red points, we could specify that in geom_point() as well.

ggplot(ay_means, aes(x=dob, y=F1_n)) +
  geom_point(color = "red", size = 3)

We still need to be sure to map the allophones to the color of the points, though. We’ll do this in the data layer.

p <- ggplot(ay_means, aes(x=dob, y=F1_n, color = plt_vclass)) +
    geom_point()
p

We can see a few of the default setting of ggplot2 on display here. Most striking is the light grey background, with white grid lines. Opinion varies on whether or not this is aesthetically or technically pleasing, but don’t worry, it’s adjustable.

Another default is to label the x and y axes with the column names from the data frame. I’ll inject a bit of best practice advice here, and tell you to always change the axis names. It’s nearly guaranteed that your data frame column names will make for very poor axis labels. We’ll cover how to do that shortly.

Finally, note that we didn’t need to tell geom_point() about the x and y axes. This may seem trivial, but it’s a really important, and powerful aspect of ggplot2. When you add any layer at all to a plot, it will inherit the data-to-aesthetic mappings which were defined in the data layer. We’ll discuss inheritance, and how to override, or define new data-to-aesthetic mappings within any geom.

The statistics layer

The final figure also includes a smoothing line, which is one of many possible statistical layers we can add to a plot.

  p <- p + stat_smooth()
  p

We’ll go over the default behavior of stat_smooth() below, but in this plot, the smoothing line represents a loess smooth, and the semi-transparent ribbon surrounding the solid line is the 95% confidence interval.

One important thing to realize is that it’s not necessary to include the points in order to add a smoothing line. Here’s what the plot would look like with the points omitted.

 ggplot(ay_means, aes(x=dob, y=F1_n, color = plt_vclass)) +
  stat_smooth()

Notice how the y-axis has zoomed in to just include the range of the smoothing line and standard error.

Scale transformations

I also wanted to make some alterations to the default y axis scales. The y-axis is currently running in reverse to the intuitive direction of F1. Higher vowels have lower F1 values, so we want to flip the y-axis. I also want to change the color scale, and its labels.

p <- p + scale_y_reverse()+
        scale_color_brewer(palette = "Dark2",
                           labels = c("voiced","voiceless"))
p

It’s worth noting that the smoothing line here is calculated over the transformed data.

The other kind of scale transformation you’re most likely to make would be use a log scale on data like durations:

ggplot(ay, aes(dur))+
    stat_bin()

ggplot(ay, aes(dur))+
    stat_bin()+
    scale_x_log10()

Cosmetic alterations

Finally, I wanted to make some cosmetic adjustments to the plot. For example, the axis labels all need to be renamed. I also added a title to the plot, and changed the color theme to black and white.

p <- p + labs(x = "Date of Birth",
              y = "Normalized F1",
              color = "ay/_")+
         theme_bw()+
        ggtitle("Change in /ay/ allophones")
p

Here’s how a similar version of this plot looks in print

Futher reading and exploration

For further reading on how to use ggplot2, specifically, I’d highly recommend Kieran Healy’s new book Data Visualization: A Practical Introduction.

I’d also suggest checking out:

For more general data visualization reading, you really need to at least have an opinion about Edward Tufte’s Visual Display of Quantitative Information

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIFdvcmtzaG9wIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgY29kZV9mb2xkaW5nOiBub25lCiAgICBjc3M6IGN1c3RvbS5jc3MKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAzCmRhdGU6ICIzIEFwcmlsIDIwMTkiCmF1dGhvcjogIltKb3NlZiBGcnVlaHdhbGRdKGh0dHBzOi8vam9mcmh3bGQuZ2l0aHViLmlvKSIKLS0tCgoKCgojIFNldHRpbmcgVXAgRm9yIFRvZGF5CgpXZWxjb21lIHRoZSBEYXRhIFZpc3VhbGl6YXRpb24gV29ya3Nob3AhIERhdGEgVml6IHBsYXlzIGEgcmVhbGx5IGNydWNpYWwgcm9sZSBpbiB0aGUgbW9yZSBnZW5lcmFsIGRhdGEgYW5hbHlzaXMgd29ya2Zsb3c6CgoKCjxkaXYgY2xhc3MgPSAiaGFsZi1pbWciPgohW10oZmlndXJlcy93b3JrZmxvdy5zdmcpCjwvZGl2PgoKQWRlcXVhdGVseSB0ZWFjaGluZyBob3cgdG8gZG8gZGF0YSBzdW1tYXJpemF0aW9uIHdvdWxkIGJlIGEgd2hvbGUgZW50aXJlbHkgZGlmZmVyZW50IHdvcmtzaG9wLiBZb3UnbGwgc3RpbGwgYmUgYWJsZSB0byB3b3JrIGFsb25nIGhlcmUsIGJ1dCBpZiB5b3UncmUgdW5mYW1pbGlhciB3aXRoIFIgYW5kIHRoZSB0aWR5dmVyc2UsIHlvdSBtaWdodCBoYXZlIHRvIHJlc29ydCB0byBqdXN0IGNvcHktcGFzdGluZyBjb2RlIGFzIHdlIGdvIGFsb25nLiAKCkJ1dCwgZm9yIG1hdGVyaWFscyBmb3IgbGVhcm5pbmcgbW9yZSBhYm91dCB0aGUgYmFzaWNzIG9mIFIsIEknZCByZWZlciB5b3UgdG8gbXkgMjAxNyBMU0EgY291c2UgbWF0ZXJpYWxzOgoKMS4gW0ludHJvIHRvIFJdKGh0dHBzOi8vam9mcmh3bGQuZ2l0aHViLmlvL3RlYWNoaW5nL2NvdXJzZXMvMjAxN19sc2EvbGVjdHVyZXMvU2Vzc2lvbl8xLm5iLmh0bWwpCjIuIFtEYXRhIGFuZCBEYXRhRnJhbWVzXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fMi5uYi5odG1sKQozLiBbU3BsaXQtQXBwbHktQ29tYmluZV0oaHR0cHM6Ly9qb2ZyaHdsZC5naXRodWIuaW8vdGVhY2hpbmcvY291cnNlcy8yMDE3X2xzYS9sZWN0dXJlcy9TZXNzaW9uXzMubmIuaHRtbCkKCi0tLS0tLS0tLS0tLS0tCgojIyBUaGUgcHJvY2VzcyBvZiBsZWFybmluZyBSIAoKVGhlc2UgYXJlIHNvbWUgb2YgdGhlIGNvcmUgYXJlYXMgSSBmaWd1cmUgYXJlIG5lY2Vzc2FyeSB0byBnZXR0aW5nIGdvb2QgYXQgc3RhdGlzdGljYWwgbW9kZWxsaW5nIGluIFI6CgoxLiBVc2luZyBSIChhbmQgUlN0dWRpbykgd2VsbAoyLiBGZWVsaW5nIGNvbWZvcnRhYmxlIGFuZCBmbHVpZCByZW9yZ2FuaXppbmcgYW5kIHN1bW1hcml6aW5nIGRhdGEKMy4gKipWaXN1YWxpemluZyBEYXRhKioKNC4gRGVjaWRpbmcgYmVmb3JlIHlvdSBtb2RlbCB3aGF0IHlvdSB3YW50IHRvIGNvbXBhcmUgdG8gd2hhdAo1LiBIb3cgdG8gdHJhbnNsYXRlIHlvdXIgYW5hbHlzaXMgZ29hbHMgaW50byBSIGNvZGUKNS4gVW5kZXJzdGFuZGluZyBhIGxpdHRsZSBiaXQgYWJvdXQgc3RhdGlzdGljcwo2LiBXaGVuIHNvbWV0aGluZyBnb2VzIHdyb25nLCBiZWluZyBhYmxlIHRvIGFjY3VyYXRlbHkgYXR0cmlidXRlIHlvdXIgZGlmZmljdWx0eSB0byBvbmUgb2YgdGhlIGFib3ZlIHRvcGljcwoKVGhlc2UgYXJlIGFsbCBza2lsbHMgeW91IGNhbiBhY2hpZXZlIHRocm91Z2ggcHJhY3RpY2UsIGV4cGVyaWVuY2UsIGFuZCBvY2Nhc2lvbmFsIGd1aWRhbmNlIGZyb20gc29tZW9uZSBtb3JlIHNraWxsZWQgdGhhbiB5b3UuIEl0IGlzIGV4YWN0bHkgbGlrZSBhY3F1aXJpbmcgYW55IG90aGVyIHNraWxsIG9yIGNyYWZ0LiBBdCBmaXJzdCBpdCB3aWxsIGJlIGNvbmZ1c2luZywgeW91J2xsIG1ha2Ugc29tZSBtaXN0YWtlcywgYW5kIGl0IHdvbid0IGxvb2sgc28gZ29vZC4gSSBsaWtlIGNvbXBhcmluZyBpdCB0byBrbml0dGluZy4KCjxkaXYgc3R5bGU9IndpZHRoOjEwMCU7ZmxvYXQ6bGVmdDsiPgo8ZGl2IHN0eWxlID0gIndpZHRoOjM1JTtmbG9hdDpsZWZ0O21hcmdpbi1sZWZ0OjEwJTttYXJnaW4tcmlnaHQ6NSU7bWFyZ2luLWJvdHRvbTo1JTsiPgoKVGhlIGZpcnN0IGhhdCBJIGV2ZXIga25pdDoKCiFbXShmaWd1cmVzL2ZpcnN0aGF0LmpwZykKCjwvZGl2PgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDozNSU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87bWFyZ2luLXJpZ2h0OjEwJTttYXJnaW4tbGVmdDo1JTttYXJnaW4tYm90dG9tOjUlOyI+CgpBIG1vcmUgcmVjZW50IGhhdCBJIGtuaXQ6IAoKIVtdKGZpZ3VyZXMvbGFzdGhhdC5qcGcpCgo8L2Rpdj4KCgo8L2Rpdj4KCgpUaGUgd2F5IEkgaW1wcm92ZWQgbXkga25pdHRpbmcgaXMgZXhhY3RseSB0aGUgc2FtZSBhcyBob3cgeW91IGNhbiBpbXByb3ZlIHlvdXIgUiBwcm9ncmFtbWluZyBhYmlsaXR5OgoKKiBJIGtuaXQgYSBsb3QgKGFsbW9zdCBldmVyeSBkYXkpLgoqIEkgbWVtb3JpemVkIGEgYnVuY2ggb2Ygc3R1ZmYuCiogUmVtZW1iZXJlZCB3aGVyZSB0byBsb29rIHVwIHRoZSBzdHVmZiBJIGRvbid0IGhhdmUgbWVtb3JpemVkLgoqIE15IGtuaXR0aW5nIGJlY2FtZSBtb3JlICJpZGlvbWF0aWMiIChpLmUuIEkgc3RhcnRlZCBrbml0dGluZyBsaWtlIGhvdyBvdGhlciBrbml0dGVycyBrbml0KS4KKiBJIGxlYXJuZWQgaG93IHRvIGlkZW50aWZ5IGFuZCBmaXggbWlzdGFrZXMgd2l0aG91dCB1bmRvaW5nIG15IGVudGlyZSBwcm9qZWN0LgoqIEkgZGV2ZWxvcGVkIGdvb2Qgd29ya3NwYWNlIGh5Z2llbmUgJiBvcmdhbml6YXRpb24uCiogQXMgSSBnb3QgdGhlIGJhc2ljcyBkb3duLCBJIHN0YXJ0ZWQgcmVzZWFyY2hpbmcgYW5kIGluY29ycG9yYXRpbmcgZnVzc3kgbGl0dGxlIGRldGFpbHMgaW50byBteSB3b3JrLgoKCiMjIFIsIFJTdHVkaW8gYW5kIFIgTm90ZWJvb2tzCgpXZSdyZSBnb2luZyB0byBiZSB1c2luZyBSLCBSU3R1ZGlvLCBhbmQgUiBOb3RlYm9va3MgaW4gdGhpcyBjb3Vyc2UsIGFuZCBpdCdzIGEgbGl0dGxlIGltcG9ydGFudCB0byBrZWVwIHN0cmFpZ2h0IHdoYXQgdGhlc2UgdGhyZWUgdGhpbmdzIGFyZToKCiMjIyBSCgoqKlIqKiBpcyBhIHByb2dyYW1taW5nIGxhbmd1YWdlIHRoYXQgcnVucyBvbiB5b3VyIGNvbXB1dGVyLiBBdCBpdHMgYmFyZXN0IGJvbmVzLCBpdCBsb29rcyBsaWtlIHRoaXM6Cgo8ZGl2IGNsYXNzID0gImhhbGYtaW1nIj4KIVtdKGZpZ3VyZXMvMl9fUi5wbmcpCjwvZGl2PgoKWW91IGNhbiB0eXBlIHRleHQgaW50byB0aGUgcHJvbXB0IHRoZXJlLCBhbmQgaWYgeW91J3ZlIHN1Y2Nlc3NmdWxseSBtZW1vcml6ZWQgdGhlIHJpZ2h0IFIgY29tbWFuZHMsIGl0J2xsIGRvIHNvbWUgdGhpbmdzLgoKCiMjIyBSU3R1ZGlvCgoqKlJTdHVkaW8qKiBpcyBsaWtlIGFuIEluc3RhZ3JhbSBmaWx0ZXIgb3ZlciB0byBvZiBSLCB0byBtYWtlIHlvdXIgUiB1c2UgZXhwZXJpZW5jZSBiZXR0ZXIuIEl0IHZpc3VhbGx5IG9yZ2FuaXplcyBzb21lIGltcG9ydGFudCBjb21wb25lbnRzIG9mIHVzaW5nIFIgaW50byBwYW5lcywgYW5kIG9mZmVycyAqY29kZSBjb21wbGV0aW9uKiBzdWdnZXN0aW9ucy4gRm9yIGV4YW1wbGUsIGlmIHlvdSBlbWJlciB0aGVyZSdzIHNvbWV0aGluZyBjYWxsZWQgYSAiV2lsY294b24gdGVzdCIsIGJ1dCB5b3UgZG9uJ3QgcmVtZW1iZXIgd2hhdCB0aGUgZnVuY3Rpb24gaW4gUiBpcywgeW91IGNhbiBzdGFydCB0eXBpbmcgaW4gYFdpbGNgLCBhbmQgdGhpcyB3aWxsIGhhcHBlbjoKCjxkaXYgY2xhc3MgPSAiaGFsZi1pbWciPgohW10oZmlndXJlcy9jb2RlQ29tcGxldGlvbi5wbmcpCjwvZGl2PgoKUlN0dWRpbydzIGF1dG9jb21wbGV0aW9uIGlzIHJlYWxseSB1c2VmdWwgZm9yIGEgbG90IG9mIG90aGVyIHRoaW5ncywgbGlrZSByZW1pbmRpbmcgeW91IHdoYXQgdGhlIGNvbHVtbiBuYW1lcyBhcmUgaW4geW91ciBkYXRhIGZyYW1lLCB3aGF0IHRoZSBuYW1lcyBvZiBhbGwgdGhlIGFyZ3VtZW50cyB0byBhIGZ1bmN0aW9uIGFyZSwgZXRjLiAKCkJ1dCBwZXJoYXBzIHRoZSBtb3N0IHZhbHVhYmxlIGNvbXBvbmVudCBpbiBSIFN0dWRpbyB0aGVzZSBkYXlzIGlzIGl0cyBhdXRob3JpbmcgdG9vbHMsIGxpa2UgUiBOb3RlYm9va3MKCiMjIyBSIE5vdGVib29rcwoKUiBOb3RlYm9va3MgYWxsb3cgeW91IHRvIGRvY3VtZW50IHlvdXIgY29kZSBpbiBwbGFpbiB0ZXh0LCBpbnNlcnQgUiBDb2RlIGNodW5rcywgYW5kIHZpZXcgdGhlIHJlc3VsdHMgb2YgdGhlIFIgY29kZSBhbGwgaW4gb25lIHBsYWNlLCB0aGVuIGNvbXBpbGUgaXQgaW50byBhIG5pY2UgbG9va2luZyBub3RlYm9vay4KCgojIyMgRGlzY3Vzc2lvbgoKSSdtIGdvaW5nIHRvIHJlY29tbWVuZCAoZm9yIG5vdyBhdCBsZWFzdCkgdGhhdCB5b3UgcnVuIGFsbCBvZiB5b3VyIGNvZGUgdGhvdWdoIGFuIFIgTm90ZWJvb2suIEl0IGlzIHBvc3NpYmxlIHRvIGp1c3QgdHlwZSB0aGluZ3MgaW50byB0aGUgUiBjb25zb2xlLCBidXQgdGhhdCdzIGtpbmQgb2YgbGlrZSBkaWN0YXRpbmcgYSBwYXBlciBpbnRvIHRoaW4gYWlyLiBPbmNlIHlvdSd2ZSBzcG9rZW4gdGhlIHdvcmRzLCB0aGV5IGRpc2FwcGVhciBhbmQgY2FuIGJlIGhhcmQgdG8gcmVjb3Zlci4KCk15IGVhcmxpZXIgYWR2aWNlIHdvdWxkIGhhdmUgYmVlbiB0byB3cml0ZSBhbGwgb2YgeW91ciBjb2RlIGluIGFuIFIgc2NyaXB0IGZpbGUsIGJ1dCB0aGF0IGFsc28gc2VwYXJhdGVzIHRoZSBjb2RlIGZyb20gaXRzIHJlc3VsdHMsIHdoaWNoIGNhbiBiZSBoYXJkIGZvciBiZWdpbm5lcnMgdG8ga2VlcCB0cmFjayBvZi4gCgo8aHIgPjwvaHI+CgoKIyMgSW5zdGFsbGluZyBSIFBhY2thZ2VzCgpSIGNvbWVzIHdpdGggYSBsb3Qgb2YgZnVuY3Rpb25hbGl0eSBpbnN0YWxsZWQsIGJ1dCBvbmUgd2F5IHRoYXQgUiBpcyBleHRlbnNpYmxlIGlzIHRocm91Z2ggdXNlcnMnIGFiaWxpdHkgdG8gY29udHJpYnV0ZSBuZXcgY29kZSAmICBkYXRhIHRocm91Z2ggaXQncyBwYWNrYWdlIG1hbmFnZW1lbnQgc3lzdGVtLiBXZSdyZSBnb2luZyB0byB1c2luZyBhIG51bWJlciBvZiB0aGVzZSBwYWNrYWdlcyBpbiB0aGUgY291cnNlLCBlc3BlY2lhbGx5IHNpbmNlIGEgZmV3IG9mIHRoZW0gaGF2ZSBmdW5kYW1lbnRhbGx5IGNoYW5nZWQgdGhlIHdheSBSIHByb2dyYW1taW5nIHdvcmtzIGluIHRoZSBwYXN0IDMgeWVhcnMuICBUaGVyZSdzIGFsc28gYSBjb3Vyc2UgUiBwYWNrYWdlIEkndmUgY3JlYXRlZCB0byBlYXNpbHkgZGlzdHJpYnV0ZSBzYW1wbGUgZGF0YXNldHMuCgpIZXJlJ3MgYSBiYXNpYyBkaWFncmFtIG9mIGhvdyBSIHBhY2thZ2VzIHdvcms6CgohW10oZmlndXJlcy9jcmFuX3BhY2thZ2UucG5nKQogCgoKIyMjIEluc3RhbGxpbmcgUGFja2FnZXMKCiMjIyMgYGluc3RhbGwucGFja2FnZXMoKWAKCk1vc3QgUiBwYWNrYWdlcyBhcmUgZGlzdHJpYnV0ZWQgdGhyb3VnaCBDUkFOIChDb21wcmVoZW5zaXZlIFIgQXJjaGl2ZSBOZXR3b3JrKS4gV2hlbiB5b3UgcnVuIGZ1bmN0aW9uIGBpbnN0YWxsLnBhY2thZ2VzKCJ4IilgLCBSIGNoZWNrcyB3aGV0aGVyIHRoZSBwYWNrYWdlIGAieCJgIGV4aXN0cyBvbiBDUkFOLCBhbmQgaW5zdGFsbHMgaXQgb24geW91ciBjb21wdXRlciBpZiBpdCBkb2VzLiBZb3UgbWF5YmUgYXNrZWQgdG8gY2hvb3NlIGEgIkNSQU4gbWlycm9yIiB0aGUgZmlyc3QgdGltZSB5b3UgcnVuIGBpbnN0YWxsLnBhY2thZ2VzKClgLiBUaGlzIGlzIGJlY2F1c2UgdGhlcmUgYXJlIG1hbnkgY29waWVzIG9mIENSQU4gZGlzdHJpYnV0ZWQgYWNyb3NzIHRoZSBpbnRlcm5ldC4gSSdkIHJlY29tbWVuZCBjaG9vc2luZyB0aGUgZmlyc3Qgb3B0aW9uIGNhbGxlZCBgMC1DbG91ZGAuCgoKIyMjIyBgaW5zdGFsbF9naXRodWIoKWAKCkFzIGEgcGFja2FnZSBkZXZlbG9wZXIsIGdldHRpbmcgYSBwYWNrYWdlIG9udG8gQ1JBTiBjYW4gYmUgYSBiaXQgb2YgYSBwYWluLCBzbyBzb21lIHBhY2thZ2VzIChhbmQgZGV2ZWxvcG1lbnQgdmVyc2lvbnMgb2YgbWFueSkgYXJlIGFsc28gYXZhaWxhYmxlIG9uIEdpdEh1Yiwgd2hpY2ggY2FuIGJlIGVhc2lseSBpbnN0YWxsZWQgd2l0aCBgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJ1c2VybmFtZS9wYWNrYWdlIilgLgoKCiMjIyBJbnN0YWxsaW5nIHBhY2thZ2VzIGlzIGRpZmZlcmVudCBmcm9tIGxvYWRpbmcgcGFja2FnZXMKCioqSW5zdGFsbGluZyoqIGEgcGFja2FnZSBpcyBkaWZmZXJlbnQgZnJvbSAqKmxvYWRpbmcqKiBwYWNrYWdlcy4gSW5zdGFsbGluZyBhIHBhY2thZ2Ugb25seSBkb3dubG9hZHMgYW5kIGNvbmZpZ3VyZXMgdGhlIGNvZGUgb24geW91ciBjb21wdXRlci4gSW4gb3JkZXIgdG8gKnVzZSogdGhlIGNvbnRlbnRzIG9mIGEgcGFja2FnZSwgeW91IG5lZWQgdG8gbG9hZCBpdCBpbnRvIHlvdXIgUiBzZXNzaW9uIHdpdGggYGxpYnJhcnkoKWAuCgotIFlvdSBvbmx5IG5lZWQgdG8gcnVuIGBpbnN0YWxsLnBhY2thZ2VzKClgIG9uY2UgdG8gaW5zdGFsbCBhIHBhY2thZ2UsIG9yIHRvIHVwZGF0ZSBhIHBhY2thZ2UuCi0gWW91IG5lZWQgdG8gcnVuIGBsaWJyYXJ5KClgIGF0IHRoZSBzdGFydCBvZiBldmVyeSBuZXcgUiBzZXNzaW9uIGluIG9yZGVyIHRvIHVzZSB0aGUgZnVuY3Rpb25hbGl0eSBmcm9tIHRoYXQgcGFja2FnZS4KCkZvciBleGFtcGxlLCBgZ2dwbG90KClgIGlzIGEgZnVuY3Rpb24gZnJvbSB0aGUgcGFja2FnZSBgZ2dwbG90MmAuIEkgaGF2ZSBhbHJlYWR5IGluc3RhbGxlZCBgZ2dwbG90MmAgb24gbXkgY29tcHV0ZXIsIGJ1dCBpZiBJIHRyeSB0byB1c2UgYGdncGxvdCgpYCBiZWZvcmUgbG9hZGluZyB0aGUgcGFja2FnZSB3aXRoIGBsaWJyYXJ5KClgLCBJJ2xsIGdldCB0aGUgZXJyb3IgdGhhdCB0aGUgZnVuY3Rpb24gd2FzIG5vdCBmb3VuZC4KCmBgYHtyfQpmb28gPC0gZ2dwbG90KCkKYGBgCgpgYGB7cn0KbGlicmFyeSgiZ2dwbG90MiIpCmZvbyA8LSBnZ3Bsb3QoKQpgYGAKCgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzPSJiaWctbGFiZWwiPn4yIE1pbnV0ZSBBY3Rpdml0eTwvc3Bhbj4KTGV0J3MgYWN0dWFsbHkgbG9hZCB1cCB0aGUgaW5pdGlhbCBwYWNrYWdlcyB3ZSdyZSBnb2luZyB0byB1c2UgdG9kYXk6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHNhMjAxNykKbGlicmFyeShicm9vbSkKYGBgCgo8L2Rpdj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIEZpcnN0IFByaW5jaXBsZXMgb2YgUGxvdHRpbmcKCiMjIFdoeSBQbG90CgpEYXRhIHZpc3VhbGl6YXRpb24gaXMgYW4gKmVzc2VudGlhbCogY29tcG9uZW50IG9mIGRhdGEgYW5hbHlzaXMuIEluIGZhY3QsIEkgYmVsaWV2ZSBpdCBpcyBuZWNlc3NhcnkgdG8gbGVhcm4gaG93IHRvIHBsb3QgeW91ciBkYXRhIGJlZm9yZSB5b3UgbGVhcm4gaG93IHRvIGRvIHN0YXRpc3RpY2FsIG1vZGVsbGluZy4gSWYgeW91IGhhdmVuJ3QgbWFkZSBhICpsb3QqIG9mIGdyYXBocyBvZiB5b3VyIGRhdGEsIGFuZCBoYXZlIG9ubHkgbG9va2VkIGF0IGF2ZXJhZ2VzLCBjb3JyZWxhdGlvbnMsIGFuZCBsaW5lYXIgbW9kZWwgcmVzdWx0cywgdGhhdCB5b3UgZG9uJ3QgcmVhbGx5IHVuZGVyc3RhbmQgeW91ciBkYXRhLiAKClRoZXJlJ3MgYSBjbGFzc2ljIGlsbHVzdHJhdGlvbiBvZiB0aGlzIGNhbGxlZCBBbnNjb21iZSdzIHF1YXJ0ZXQsIHdoaWNoIHdoZW4gcGxvdHRlZCBsb29rcyBsaWtlIHRocmVlIHZlcnkgZGlzdGluY3RpdmUgcGF0dGVybnMuCgpgYGB7ciBlY2hvID0gRn0KdGlkeV9hbnNjb21iZSA8LSBhbnNjb21iZSAlPiUKICAgICAgICAgICAgICAgICAgICBtdXRhdGUoaWR4ID0gMTpuKCkpICU+JQogICAgICAgICAgICAgICAgICAgIGdhdGhlcihrZXksIHZhbHVlLCB4MTp5NCkgJT4lCiAgICAgICAgICAgICAgICAgICAgc2VwYXJhdGUoa2V5LCBpbnRvID0gYygidmFyaWFibGUiLCAic2VyaWVzIiksIHNlcCA9IDEpICU+JQogICAgICAgICAgICAgICAgICAgIHNwcmVhZCh2YXJpYWJsZSwgdmFsdWUpCgp0aWR5X2Fuc2NvbWJlICU+JQogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgICBnZW9tX3BvaW50KHNpemUgPSAzKSArIAogICAgZmFjZXRfd3JhcCh+c2VyaWVzKQpgYGAKCkJ1dCBpZiB5b3UgZml0IGxpbmVhciBtb2RlbHMgdG8gdGhlbSwgdGhleSBoYXZlIG5lYXJseSBpZGVudGljYWwgc3RhdGlzdGljYWwgcHJvcGVydGllcy4KCmBgYHtyfQpmaXRfbG0gPC0gZnVuY3Rpb24oZGYpewogIGxtKHkgfiB4LCBkYXRhID0gZGYpCn0KCmFuc2NvbWJfbW9kZWxzIDwtIHRpZHlfYW5zY29tYmUgJT4lCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoc2VyaWVzKSAlPiUKICAgICAgICAgICAgICAgICAgICBuZXN0KCkgJT4lCiAgICAgICAgICAgICAgICAgICAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZpdF9sbSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX3BhcmFtX2RmID0gbWFwKG1vZGVsLCB0aWR5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZ2xhbmNlID0gbWFwKG1vZGVsLCBnbGFuY2UpKQoKYW5zY29tYl9tb2RlbHMgJT4lCiAgdW5uZXN0KG1vZGVsX3BhcmFtX2RmKSAlPiUKICBhcnJhbmdlKHRlcm0pCmBgYApgYGB7cn0KYW5zY29tYl9tb2RlbHMgJT4lCiAgdW5uZXN0KG1vZGVsX2dsYW5jZSkKYGBgCgpJdCBoYXMgYmVlbiBldmVuIG1vcmUgaHVtb3JvdXNseSBpbGx1c3RyYXRlZCByZWNlbnRseSB0aGF0IHlvdSBjYW4gcHJvZHVjZSBkYXRhIHNldHMgb2YgYWxtb3N0IGFueSBhcmJpdHJhcnkgc2hhcGUgdGhhdCBoYXZlIG5lYXJseSBpZGVudGljYWwgc3RhdGlzdGljYWwgcHJvcGVydGllcy4KCiFbXShmaWd1cmVzL0Rpbm9TZXF1ZW50aWFsU21hbGxlci5naWYpCltTYW1lIFN0YXRzLCBEaWZmZXJlbnQgR3JhcGhzOiBHZW5lcmF0aW5nIERhdGFzZXRzIHdpdGggVmFyaWVkIEFwcGVhcmFuY2UgYW5kIElkZW50aWNhbCBTdGF0aXN0aWNzIHRocm91Z2ggU2ltdWxhdGVkIEFubmVhbGluZ10oaHR0cHM6Ly93d3cuYXV0b2Rlc2tyZXNlYXJjaC5jb20vcHVibGljYXRpb25zL3NhbWVzdGF0cykKCkluIGZhY3QsIG1vcmUgYW5kIG1vcmUgcGVvcGxlIGFyZSBzdGFydGluZyB0byB1c2Ugc3RhdGlzdGljYWwgdGVjaG5pcXVlcyB3aGVyZSB0aGUgKm9ubHkqIHBsYXVzaWJsZSB3YXkgdG8gcmVwb3J0IHRoZSByZXN1bHRzIG9mIHRoZSBtb2RlbCBpcyB3aXRoIGEgZ3JhcGhpY2FsIGRpc3BsYXkuCgpgYGB7ciBlY2hvID0gRn0KbGlicmFyeShtZ2N2KQpsaWJyYXJ5KGl0c2FkdWcpCmBgYAoKYGBge3IgZWNobyA9IEZ9CmF5X3RlbXAgPC0gYXkgJT4lCiAgICAgICAgICAgIGZpbHRlcihmb2xfc2VnICVpbiUgYygiVCIsICJEIiksCiAgICAgICAgICAgICAgICAgICBjb250ZXh0ID09ICJpbnRlcm5hbCIpICU+JQogICAgICAgICAgICBtdXRhdGUoZG9iID0geWVhci1hZ2UsCiAgICAgICAgICAgICAgICAgICBwbHRfdmNsYXNzID0gZmFjdG9yKHBsdF92Y2xhc3MpLAogICAgICAgICAgICAgICAgICAgd29yZCA9IGZhY3Rvcih3b3JkKSwKICAgICAgICAgICAgICAgICAgIGlkc3RyaW5nID0gZmFjdG9yKGlkc3RyaW5nKSwKICAgICAgICAgICAgICAgICAgIGxvZzJkdXIgPSBsb2cyKGR1ciksCiAgICAgICAgICAgICAgICAgICBkdXJfYyA9IGxvZzJkdXIgLSBtZWRpYW4obG9nMmR1cikpCmF5X2JhbSA8LSBiYW0oRjFfbiB+IHBsdF92Y2xhc3MgKyBzKGRvYiwgYnkgPSBwbHRfdmNsYXNzKSsKICAgICAgICAgICAgICAgIHMobG9nMmR1ciwgYnkgPSBwbHRfdmNsYXNzKSsKICAgICAgICAgICAgICAgIHRlKGRvYiwgbG9nMmR1ciwgYnkgPSBwbHRfdmNsYXNzKSsKICAgICAgICAgICAgICAgIHMoaWRzdHJpbmcsIGJzID0gJ3JlJykgKyBzKHdvcmQsIGJzID0gJ3JlJyksCiAgICAgICAgICAgICAgZGF0YSA9IGF5X3RlbXApCmBgYAoKYGBge3IgZWNobyA9ICBGfQpwbG90X2RpZmYoYXlfYmFtLHZpZXcgPSAiZG9iIiwgY29tcCA9IGxpc3QocGx0X3ZjbGFzcyA9IGMoImF5IiwgImF5MCIpKSwgcm0ucmFuZWYgPSBULCBwcmludC5zdW1tYXJ5ID0gRikKYGBgCgoKIyMgVGhpbmtpbmcgYWJvdXQgUGxvdHRpbmcKCkl0J3MgaW1wb3J0YW50IHRvIHRoaW5rIG9mIHlvdXIgZmlndXJlcyBhcyBhICpyZXBvcnQqIG9mIHlvdXIgZGF0YS4gVHJ5IHRvIHRha2UgYXMgbXVjaCBjYXJlIGluIHByb2R1Y2luZyB5b3VyIHBsb3RzIGFzIHlvdSBkbyB5b3VyIHdyaXRpbmcsIG9yIHJlcG9ydGluZyBvZiB5b3VyIHN0YXRpc3RpY3MuIFRoZXkgYXJlIGFzIGltcG9ydGFudCBhcyAob3IgZm9yIHNvbWUgcmVhZGVycywgbW9yZSBpbXBvcnRhbnQgdGhhbikgYW55dGhpbmcgZWxzZSBpbiB5b3VyIHBhcGVyLgoKCiMjICJBY2N1cmFjeSIKCldoZW4gbWFraW5nIGEgcGxvdCwgeW91IHNob3VsZCBzdHJpdmUgZm9yIGFjY3VyYWN5IGluOgoKLSBBY2N1cmF0ZWx5IHJlcHJlc2VudGluZyB0aGUgcHJvcGVydGllcyBvZiBudW1iZXJzLgotIEFjY3VyYXRlbHkgcmVwcmVzZW50aW5nIHRoZSBuYXR1cmUgb2YgeW91ciBkYXRhLgoKVGFrZSB0aGlzIHZlcnkgc2ltcGxlIGRhdGEgc2V0OgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo1MCUiPgoKfCBncm91cCB8IHZhbHVlIHwKfCAtLS0tOiB8IC0tLS06IHwKfCBBIHwgMiB8CnwgQiB8IDUgfAoKPC9kaXY+CgpGb3Igb3VyIHB1cnBvc2VzLCB0aGVzZSBudW1iZXJzIGhhdmUgdGhyZWUgcHJvcGVydGllcy4KCjEuICoqT3JkZXIqKjogMiA8IDUsIG9yIEEgPCBCCjIuICoqTWFnbml0dWRlKio6IDUgPSAyLjUgJFx0aW1lcyQgMiwgb3IgQiA9IDIuNSAkXHRpbWVzJCBBCjMuICoqQ29udGV4dHVhbCBNYWduaXR1ZGUqKjogSWYgQSBhbmQgQiBhcmUgYmFycywgYW5kIHRoZXNlIGFyZSBtZWFzdXJlIG9mIHRoZSBjb3N0IG9mIGEgcGludCwgdGhlbiBBIG11c3QgYmUgYSByZWFsIGRpdmUgKGFuZCBhIGdvb2QgZGVhbCksIGFuZCBCIG11c3QgYmUgYSBsaXR0bGUgYml0IGJldHRlciwgYnV0IHN0aWxsIG5vdCB0b28gZmFuY3kuIElmIEEgYW5kIEIgYXJlIHBlb3BsZSwgYW5kIHRoZXNlIGFyZSB0aGVpciBudW1iZXIgb2YgbGVncywgdGhlbiBBIGhhcyBhbiB1bnN1cnByaXNpbmcgbnVtYmVyIG9mIGxlZ3MgYW5kIEIgaGFzIGEgc3VycHJpc2luZyBudW1iZXIgb2YgbGVncy4KCkhlcmUgaXMgYW4gZXhhbXBsZSBvZiBhbiBpbmFjY3VyYXRlIHBsb3Q6CgpgYGB7ciBmaWcud2lkdGggPSA1LzIsIGZpZy5oZWlnaHQgPSA1LzIsIGVjaG8gPSBGfQpudW0gPC0gZGF0YS5mcmFtZShncm91cCA9IGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICB2YWx1ZSA9IGMoMiwgNSkpCgpnZ3Bsb3QobnVtLCBhZXMoZ3JvdXAsIHZhbHVlKSkrCiAgICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBncm91cCwgeT0xLjUsIHllbmQ9dmFsdWUpLCBzaXplPTEwKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCkl0IHN1Y2Nlc3NmdWxseSBjYXB0dXJlcyB0aGUgKm9yZGVyKiBvZiBBIGFuZCBCLCBidXQgZmFpbHMgdG8gY2FwdHVyZSB0aGUgY29ycmVjdCAqbWFnbml0dWRlKiBvZiB0aGUgZGlmZmVyZW5jZS4gVGhlIG1hZ25pdHVkZSBvZiB0aGUgZGlmZmVyZW5jZSBpcyB0aHJvd24gb2ZmIGJlY2F1c2UgdGhlIHktYXhpcyBkb2Vzbid0IHN0YXJ0IGF0IDAuIEluIHRoaXMgcGxvdCwgdGhlIEIgbGluZSBpcyBgciAoNS0xLjUpLygyLTEuNSlgJFx0aW1lcyQgbG9uZ2VyIHRoYW4gdGhlIEEgbGluZSwgYnV0IHRoZSBhY3R1YWwgbWFnbml0dWRlIG9mIHRoZSBkaWZmZXJlbmNlIGlzIDIuNSRcdGltZXMkLiBUaGlzIHByb2R1Y2VzIGEgImxpZSBmYWN0b3IiIG9mICRcZnJhY3s3fXsyLjV9ID0gYHIgNy8yLjVgJC4KClRoaXMgaXNuJ3QganVzdCBhIGh5cG90aGV0aWNhbCBwcm9ibGVtIGVpdGhlci4gRm9yIGV4YW1wbGUsIEJyaXRpc2ggZWxlY3RvcmFsIG1haWxlcnMgYXJlIG5vdG9yaW91cyBmb3IgdGhlIGluYWNjdXJhdGVseSBwb3J0cmF5aW5nIHRoZSBtYWduaXR1ZGUgb2YgZGlmZmVyZW5jZXMuCgohW10oZmlndXJlcy9pbmFjY3VyYXRlLnBuZykKCgpgYGB7ciBlY2hvID0gRn0Kc2NvdCA8LSBkYXRhX2ZyYW1lKHBhcnR5ID0gYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU05QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGliIERlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhYm91ciIpLAogICAgICAgICAgICAgICAgICAgbXBzID0gYygxLCA3LCAxMiwgMzkpKQpnZ3Bsb3Qoc2NvdCwgYWVzKHBhcnR5LCBtcHMsIGZpbGwgPSBwYXJ0eSkpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpKwogIHhsaW0oYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgIlNOUCIsCiAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgIkxhYm91ciIpKSsKICBzY2FsZV9maWxsX21hbnVhbChsaW1pdHM9IGMoIkNvbnNlcnZhdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGliIERlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYWJvdXIiKSwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCIjMDA4N2RjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRkZGOTVEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRkRCQjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjZDUwMDAwIikpKwogICAgdGhlbWVfbWluaW1hbCgpKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpgYGAKCkJvdGggYWNhZGVtaWMgcmVzZWFyY2hlcnMgYW5kIHRoZSBwcm9kdWNlcnMgb2YgdGhlc2UgcG9saXRpY2FsIG1haWxlcnMgbWF5IGNvdW50ZXIgYnkgc2F5aW5nCgo+IEJ1dCB0aGUgYXhlcyB3ZXJlIGxhYmVsbGVkIGFjY3VyYXRlbHkhCgpJZiByZWFkZXJzIHdvdWxkIHVuZGVyc3RhbmQgeW91ciBkYXRhIGJldHRlciBpZiB0aGV5ICppZ25vcmVkIHRoZSBncmFwaGljYWwgZWxlbWVudHMgb2YgeW91ciBwbG90KiwgdGhlbiB5b3VyIHBsb3QgaXMgbmV0LW5lZ2F0aXZlIHRvIGFjY3VyYXRlIGNvbW11bmljYXRpb24sIGFuZCBJIGNhbiBvbmx5IGFzc3VtZSB0aGF0IGFjY3VyYWN5IHdhcyBuZXZlciB5b3VyIHByaW1hcnkgZ29hbCBhbnl3YXkuIAoKIyMgVGhpbmsgYWJvdXQgaG93IHBsb3RzIGFyZSByZWFkCgpUaGUgd2F5IGluIHdoaWNoIHdlIHJlYWQgdGhlIG51bWJlcnMgb2ZmIG9mIHBsb3RzIGFsc28gbWF0dGVycyBhIGxvdCB0byBjb25zdHJ1Y3RpbmcgYSBmaWd1cmUuCkZvciBleGFtcGxlLCB0aGUgYmFyIHBsb3QgYWJvdmUgbWFwcyB0aGUgbnVtYmVyIG9mIE1QcyB0byB0aGUgKmxlbmd0aCogb2YgdGhlIGJhcnMuIEFub3RoZXIgZ3JhcGhpY2FsIGZlYXR1cmUgd2UgY291bGQgdXNlIGlzIHRvIG1hcCB0aGUgbnVtYmVyIG9mIE1QcyB0byB0aGUgKnBvc2l0aW9uKiBvZiBwb2ludHMuCgpgYGB7ciBlY2hvID0gRn0KZ2dwbG90KHNjb3QsIGFlcyhwYXJ0eSwgbXBzLCBjb2xvciA9IHBhcnR5KSkrCiAgZ2VvbV9wb2ludChzaXplID0gNiwgY29sb3IgPSAiYmxhY2siKSsKICBnZW9tX3BvaW50KHNpemUgPSA1KSsKICB4bGltKHJldihjKCJDb25zZXJ2YXRpdmUiLAogICAgICAgICAiU05QIiwKICAgICAgICAgIkxpYiBEZW0iLAogICAgICAgICAiTGFib3VyIikpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwobGltaXRzPSBjKCJDb25zZXJ2YXRpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU05QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxpYiBEZW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFib3VyIiksCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiIzAwODdkYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI0ZGRjk1RCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI0ZEQkIzMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI2Q1MDAwMCIpKSsKICAgIHRoZW1lX21pbmltYWwoKSsKICAgIGNvb3JkX2ZsaXAoKSsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpPciwgd2UgY291bGQgbWFwIGl0IHRvIHRoZSAqYXJlYSogb2YgY2lyY2xlczoKCmBgYHtyIGVjaG8gPSBGfQpzY290IDwtIGRhdGFfZnJhbWUocGFydHkgPSBjKCJDb25zZXJ2YXRpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFib3VyIiksCiAgICAgICAgICAgICAgICAgICBtcHMgPSBjKDEsIDcsIDEyLCAzOSksCiAgICAgICAgICAgICAgICAgICB4MCA9IGMoMCw1LDEwLDE1KSwKICAgICAgICAgICAgICAgICAgIHkwID0gcmVwKDEsIDQpKQoKZ2dwbG90KHNjb3QpKwogICAgZ2dmb3JjZTo6Z2VvbV9jaXJjbGUoYWVzKHgwID0geDAsIHkwID0geTAsIHIgPSBzcXJ0KG1wcyksIGZpbGwgPSBwYXJ0eSksIGFscGhhID0gMC42KSsKICAgIGNvb3JkX2ZpeGVkKCkrCiAgIHNjYWxlX2ZpbGxfbWFudWFsKGxpbWl0cz0gYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNOUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhYm91ciIpLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiMwMDg3ZGMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGRkY5NUQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGREJCMzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNkNTAwMDAiKSkrCiAgICBnZ3RpdGxlKCJOdW1iZXIgb2YgTVBzIikrCiAgICB0aGVtZV92b2lkKCkKYGBgCgpPciwgdG8gdGhlICphbmdsZSogb2YgYSBwaWUgY2hhcnQgc2xpY2U6CgpgYGB7cn0KZ2dwbG90KHNjb3QsIGFlcygieCIsIG1wcywgZmlsbCA9IHBhcnR5KSkrCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJibGFjayIpKwogICAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpKwogICAgIHNjYWxlX2ZpbGxfbWFudWFsKGxpbWl0cz0gYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNOUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhYm91ciIpLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiMwMDg3ZGMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGRkY5NUQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGREJCMzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNkNTAwMDAiKSkrCiAgICB0aGVtZV92b2lkKCkrCiAgICBnZ3RpdGxlKCJQcm9wb3J0aW9uIG9mIE1QcyIpCmBgYAoKSG93ZXZlciwgbm90IGFsbCB2aXN1YWwgZGltZW5zaW9ucyBhcmUgYXMgZWFzaWxseSBkZWNvZGVkIHZpc2lhbGx5IGFzIG90aGVycy4gUmVzZWFyY2ggaGFzIGZvdW5kIHRoYXQgcGVvcGxlIGFyZSBtb3JlIGFjY3VyYXRlIGluIHRoZWlyIHBlcmNlcHRpb24gb2YgbnVtZXJpYyBkaWZmZXJlbmNlcyB3aGVuIGdyYXBocyB1c2UgKipsZW5ndGgqKiBhbmQgKipwb3NpdGlvbioqLCByYXRoZXIgdGhhbiBhcmVhLCBvciBhbmdsZS4KCiMjIENvbG8odSlyCgpXZSBhbHNvIG5lZWQgdG8gdGhpbmsgYSAqbG90KiBhYm91dCBob3cgd2UgdXNlIGNvbG9yIGluIGZpZ3VyZXMuIEluIHRoZSBwb2xpdGljYWwgZ3JhcGhzLCB0aGVyZSBhcmUgY2xlYXIgYW5kIGljb25pYyBjb2xvcnMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggcG9saXRpY2FsIHBhcnR5LCB3aGljaCBJIHdvdWxkIHJlY29tbWVuZCB1c2luZyBpbiBhIGNhc2UgbGlrZSB0aGF0LiBDaG9vc2luZyBhIGRpZmZlcmVudCBjb2xvciBwYWxldHRlIG1ha2VzIHRoZSBncmFwaCBtb3JlIGNvbmZ1c2luZy4KCmBgYHtyIGVjaG8gPSBGfQpnZ3Bsb3Qoc2NvdCwgYWVzKHBhcnR5LCBtcHMsIGZpbGwgPSBwYXJ0eSkpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpKwogIHhsaW0oYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgIlNOUCIsCiAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgIkxhYm91ciIpKSsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChsaW1pdHM9IGMoIkNvbnNlcnZhdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGliIERlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYWJvdXIiKSkrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKVGhlcmUgYXJlIG1hbnkgb3RoZXIga2luZHMgb2YgY29udmVudGlvbmFsaXplZCBjb2xvci1tZWFuaW5nIG1hcHBpbmcgdGhhdCB5b3Ugc2hvdWxkIHVzdWFsbHkgc3RpY2sgdG8gKGxpa2UgY29sZCA9IGJsdWUsIGhvdCA9IHJlZCkuIEJ1dCB5b3Ugc2hvdWxkIGFsc28gYmUgY2FyZWZ1bCB0byBhdm9pZCB1c2luZyBjb252ZW50aW9uYWxpemVkIGNvbG9yIHNjaGVtZXMgdGhhdCBlaXRoZXIgcmVlbmZvcmNlIGhhcm1mdWwgc3RlcmVvdHlwZXMgKGxpa2UgcGlua2luZyBhbmQgYmx1aW5nIGdlbmRlcikgb3IgY291bGQgYmUgb3RoZXJ3aXNlIG9mZmVuc2l2ZSB0byBjdWx0dXJhbCBzZW5zaXRpdml0aWVzLgoKCllvdSBzaG91bGQgYWxzbyBiZSBjYXJlZnVsIHRvIGF2b2lkIGFjY2lkZW50YWxseSBjb252ZXlpbmcgc29tZXRoaW5nIHlvdSBkb24ndCBpbnRlbmQgdG8gd2l0aCB5b3VyIGNvbG9yIGNob2ljZXM6CgpgYGB7ciBlY2hvID0gRn0KYXkgJT4lCiAgZmlsdGVyKHdvcmQgIT0gIkkiKSAlPiUKICBtdXRhdGUodm9pY2luZyA9IGNhc2Vfd2hlbihwbHRfdmNsYXNzID09ICJheTAiIH4gInZvaWNlbGVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dCAlaW4lIGMoImZpbmFsIiwgImNvZXh0ZW5zaXZlIil+IndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJNIiwgIk4iLCAiTkciKX4ibmFzYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJMIiwgIlIiLCAiVyIsICJZIikgfiAibGlxdWlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiW0FFSU9VXSIsIGZvbF9zZWcpfiJoaWF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAidm9pY2VkIiksCiAgICAgICAgIHZvaWNpbmcgPSBmYWN0b3Iodm9pY2luZywgbGV2ZWxzID0gYygiaGlhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaXF1aWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hc2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2b2ljZWxlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZvaWNlZCIpKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2cyKGR1ciksIGNvbG9yID0gdm9pY2luZykpKwogICAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gImJsYWNrIiwgYWVzKGdyb3VwID0gdm9pY2luZyksIHNpemUgPSAyKSsKICAgIGdlb21fZGVuc2l0eShzaXplID0gMSkrCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0VGRjNGRiIsICIjQkREN0U3IiwgIiM2QkFFRDYiLCAiIzMxODJCRCIsICIjMDg1MTlDIiwgInJlZCIpKSsKICAgIHRoZW1lX21pbmltYWwoKSsKICAgIGdndGl0bGUoIi9heS8gZHVyYXRpb24gZGlzdHJpYnV0aW9ucyIpCiAgICAKYGBgCgpUaGVyZSdzIG5vIHNlbnNpYmxlIG9yZGVyIHRvIHRoZSB2b2ljaW5nIGNvbnRleHRzIHRoYXQgL2F5LyBhcHBlYXJzIGluIGFib3ZlLCBidXQgb25lIHNlZW1zIHRvIGJlIGltcGxpZWQgdGhyb3VnaCB0aGUgdXNlIG9mIGEgZ3JhZGllbnQgY29sb3Igc2NoZW1lLiBUaGUgdm9pY2VkIGNvbnRleHQgaXMgYWxzbyBzcGVjaWFsbHkgaGlnaGxpZ2h0ZWQgYnkgYmVpbmcgbWFwZWQgdG8gYSByZWQgaHVlIHdoaWxlIHRoZSByZXN0IGFyZSBtYXBwZWQgdG8gYmx1ZSBodWVzLiBSYXRoZXIsIHdlJ2QgcHJvYmFibHkgd2FudCBhIGNvbG9yIHBhbGV0dGUgbGlrZSBvbmUgb2YgdGhlIHR3byBiZWxvdywgd2hlcmUgdGhlIGNvbG9ycyBhcmUgZGlzdGluY3QsIHVub3JkZXJlZCwgYW5kIHBlcmNlcHR1YWxseSB1bmlmb3JtLgpgYGB7ciBlY2hvID0gRn0KYXkgJT4lCiAgZmlsdGVyKHdvcmQgIT0gIkkiKSAlPiUKICBtdXRhdGUodm9pY2luZyA9IGNhc2Vfd2hlbihwbHRfdmNsYXNzID09ICJheTAiIH4gInZvaWNlbGVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dCAlaW4lIGMoImZpbmFsIiwgImNvZXh0ZW5zaXZlIil+IndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJNIiwgIk4iLCAiTkciKX4ibmFzYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJMIiwgIlIiLCAiVyIsICJZIikgfiAibGlxdWlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiW0FFSU9VXSIsIGZvbF9zZWcpfiJoaWF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAidm9pY2VkIiksCiAgICAgICAgIHZvaWNpbmcgPSBmYWN0b3Iodm9pY2luZywgbGV2ZWxzID0gYygiaGlhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaXF1aWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hc2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2b2ljZWxlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZvaWNlZCIpKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2cyKGR1ciksIGNvbG9yID0gdm9pY2luZykpKwogICAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gImJsYWNrIiwgYWVzKGdyb3VwID0gdm9pY2luZyksIHNpemUgPSAyKSsKICAgIGdlb21fZGVuc2l0eShzaXplID0gMSkrCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpKwogICAgdGhlbWVfbWluaW1hbCgpKwogICAgZ2d0aXRsZSgiL2F5LyBkdXJhdGlvbiBkaXN0cmlidXRpb25zIikKICAgIApgYGAKCgpgYGB7ciBlY2hvID0gRn0KYXkgJT4lCiAgZmlsdGVyKHdvcmQgIT0gIkkiKSAlPiUKICBtdXRhdGUodm9pY2luZyA9IGNhc2Vfd2hlbihwbHRfdmNsYXNzID09ICJheTAiIH4gInZvaWNlbGVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dCAlaW4lIGMoImZpbmFsIiwgImNvZXh0ZW5zaXZlIil+IndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJNIiwgIk4iLCAiTkciKX4ibmFzYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJMIiwgIlIiLCAiVyIsICJZIikgfiAibGlxdWlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiW0FFSU9VXSIsIGZvbF9zZWcpfiJoaWF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAidm9pY2VkIiksCiAgICAgICAgIHZvaWNpbmcgPSBmYWN0b3Iodm9pY2luZywgbGV2ZWxzID0gYygiaGlhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaXF1aWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hc2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2b2ljZWxlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIndvcmQgZmluYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZvaWNlZCIpKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2cyKGR1ciksIGNvbG9yID0gdm9pY2luZykpKwogICAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gImJsYWNrIiwgYWVzKGdyb3VwID0gdm9pY2luZyksIHNpemUgPSAyKSsKICAgIGdlb21fZGVuc2l0eShzaXplID0gMSkrCiAgICBnZ3RoZW1lczo6c2NhbGVfY29sb3JfY29sb3JibGluZCgpKwogICAgdGhlbWVfbWluaW1hbCgpKwogICAgZ2d0aXRsZSgiL2F5LyBkdXJhdGlvbiBkaXN0cmlidXRpb25zIikKICAgIApgYGAKCgoKCiMgYGdncGxvdDJgIGJhc2ljIGNvbmNlcHRzCgojIyBXaHkgbGVhcm4gYGdncGxvdDJgPwoKVGhlcmUgYXJlIGEgZmV3IGRpZmZlcmVudCBncmFwaGljcyBwYWNrYWdlcyBvdXQgdGhlcmUgdG8gdXNlLCBpbmNsdWRpbmcgYmFzZSBSIHBsb3RzIGFuZCBgbGF0dGljZWAuIGBnZ3Bsb3QyYCwgaG93ZXZlciwgaXMgbXVjaCBtb3JlIHBsdWdnZWQgaW50byB0aGUgdGlkeXZlcnNlIHdvcmtmbG93LCB3aGljaCBzZWVtcyB0byBiZSB0aGUgdHJlbmRpbmcgZGlyZWN0aW9uIG9mIFIgUHJvZ3JhbW1pbmcuIEl0J3MgYWxzbyBleHRlbnNpYmxlLCBtZWFuaW5nIHBlb3BsZSBhcmUgcHJvZHVjaW5nIFthIGxvdCBvZiByZWFsbHkgY29vbCBhbmQgcmVhbGx5IHVzZWZ1bCBhZGQgb25zXShodHRwOi8vd3d3LmdncGxvdDItZXh0cy5vcmcpISAKCgoKCgojIyBMYXllcnMsIEFlc3RoZXRpY3MsIEdlb21ldHJpZXMgYW5kIFN0YXRpc3RpY3MKCldlJ3JlIGdvaW5nIHRvIHN0YXJ0IGJ5IHdvcmtpbmcgd2l0aCB0aGUgL2F5LyBkYXRhc2V0IGZyb20gdGhlIGBsc2EyMDE3YCBwYWNrYWdlLgoKPGRpdiBjbGFzcyA9ICJib3ggYnJlYWsiPgo8c3BhbiBjbGFzcz0iYmlnLWxhYmVsIj5UaGUgL2F5LyBkYXRhc2V0PC9zcGFuPgoKVGhpcyAvYXkvIGRhdGEgc2V0IGNvbnRhaW5zIG92ZXIgODAsMDAwIHRva2VucyBvZiB0aGUgL2F5LyB2b3dlbCB3aGljaCB3ZXJlIGF1dG9tYXRpY2FsbHkgZXh0cmFjdGVkIGZyb20gMzI2IHNvY2lvbGluZ3Vpc3RpYyBpbnRlcnZpZXdzIGluIFBoaWxhZGVscGhpYS4gVGhlcmUgYXJlIHR3byBhbGxvcGhvbmVzIG9mIC9heS8gZW5jb2RlZCBpbiB0aGUgZGF0YSBjb2x1bW4gYHBsdF92Y2xhc3NgCgotIGBheTBgID0gcHJlLXZvaWNlbGVzcyAvYXkvCi0gYGF5YCA9IGFsbCBvdGhlciAvYXkvCgpBY3Jvc3MgdGhlIDIwdGggY2VudHVyeSwgdGhlIHZvd2VsIHF1YWxpdHkgb2YgcHJlLXZvaWNlbGVzcyAvYXkvIHVuZGVyd2VudCBhIGxhcmdlIGNoYW5nZSBmcm9tIHNvbWV0aGluZyBsaWtlIFvJkcmqXSB0byBbyozJql0uIFRoaXMgd2FzIGEgcGhvbmV0aWNhbGx5IGdyYWR1YWwgY2hhbmdlLCBzbyB3ZSdsbCBiZSBwbG90dGluZyBpdCBhY2NvcmRpbmcgdG8gdGhlIHByaW1hcnkgcGhvbmV0aWMgY29ycmVsYXRlIG9mIHRoaXMgY2hhbmdlLCBub3JtYWxpemVkIEYxLgo8L2Rpdj4KCgpUbyBraWNrIHRoaW5ncyBvZmYsIHdlJ2xsIGp1c3QgZXN0aW1hdGUgdGhlIGF2ZXJhZ2Ugbm9ybWFsaXplZCBGMSAKCmBgYHtyfQpheV9tZWFucyA8LSBheSAlPiUKICAgICAgICAgICAgbXV0YXRlKGRvYiA9IHllYXItYWdlKSAlPiUKICAgICAgICAgICAgZ3JvdXBfYnkoaWRzdHJpbmcsIGRvYiwgcGx0X3ZjbGFzcykgJT4lCiAgICAgICAgICAgIHN1bW1hcmlzZShGMV9uID0gbWVhbihGMV9uKSkKYGBgCgpgYGB7cn0KaGVhZChheV9tZWFucykKYGBgCgpXZSdyZSBnb2luZyB0byB0YWtlIHRoaXMgZGF0YSBhbmQgYnVpbGQgdXAgdG8gbWFraW5nIHRoaXMgcGxvdDoKCmBgYHtyIGVjaG8gPSBGLCBtZXNzYWdlID0gRiwgZmlnLndpZHRoPTgvMS4yNSwgZmlnLmhlaWdodCA9IDUvMS4yNX0KIGdncGxvdChheV9tZWFucywgYWVzKGRvYiwgRjFfbiwgY29sb3IgPSBwbHRfdmNsYXNzKSkrCiAgICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNikrCiAgICBzdGF0X3Ntb290aChzaXplID0gMSkrCiAgICBzY2FsZV95X3JldmVyc2UoKSsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIiwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJ2b2ljZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZvaWNlbGVzcyIpKSsKICAgIGxhYnMoeCA9ICJEYXRlIG9mIEJpcnRoIiwKICAgICAgICAgeSA9ICJOb3JtYWxpemVkIEYxIiwKICAgICAgICAgY29sb3IgPSAiYXkvX18iKSsKICAgIGdndGl0bGUoIkNoYW5nZSBpbiAvYXkvIGFsbG9waG9uZXMiKSsKICAgIHRoZW1lX2J3KCkKYGBgCgojIyMgTGF5ZXJzCgpZb3Ugc2hvdWxkIGhvcGVmdWxseSBzdGFydCBsb29raW5nIGF0IGZpZ3VyZXMgbGlrZSB0aGlzIG9uZSBsaWtlIG1hbnkgb2YgdXMgbG9vayBhdCB0aGUgaW1hZ2UgYmVsb3cuCgo8ZGl2IGNsYXNzID0gJ2hhbGYtaW1nJz4KIVtdKGZpZ3VyZXMvRW5saWdodDkuanBnKQo8L2Rpdj4KClRob3NlIG9mIHVzZSBmYW1pbGlhciB3aXRoIHRoaXMga2luZCBvZiBtZWRpYSBrbm93IHRoYXQgdGhlIHBpY3R1cmUgb2YgdGhlIGxpYmFyYXJ5IGlzIG5vdCB3aGF0IHdhcyBvcmlnaW5hbGx5IGNhcHR1cmUgYnkgbXkgcGhvbmUuIFJhdGhlciB0aGVyZSBhcmUgbXVsdGlwbGUgbGF5ZXJzIG9mIGVmZmVjdHMsIGZpbHRlcnMgYW5kIHRleHQgb24gdG9wIG9mIHRoZSBiYXNlIGltYWdlLCB3aGljaCBwcm9kdWNlIHRoZSBmaW5hbCBpbWFnZS4gQW5kIGluIGZhY3QsIHNvbWUgb2YgdGhlc2UgbGF5ZXJzIGFyZSBjcnVjaWFsbHkgb3JkZXJlZC4gRm9yIGV4YW1wbGUsIHRoZSB0ZXh0IHdvdWxkIGxvb2sgZGlmZmVyZW50IGlmIGl0IHdhcyBhZGRlZCB0byB0aGUgaW1hZ2UgZmlyc3QsIGFuZCB0aGVuIHRoZSBmaWx0ZXJzLCBpbnN0ZWFkIG9mIHZpY2UgdmVyc2EuCgoKU28gdG9vIHdpdGggdGhlIGBnZ3Bsb3QyYCBwbG90IGFib3ZlLiBUaGVzZSBwbG90cyBhcmUgY29uc3RydWN0ZWQgb3V0IG9mIF9fbGF5ZXJzX18uIEV2ZXJ5IGNvbXBvbmVudCBvZiB0aGUgZ3JhcGgsIGZyb20gdGhlIHVuZGVybHlpbmcgZGF0YSBpdCdzIHBsb3R0aW5nLCB0byB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gaXQncyBwbG90dGVkIG9uLCB0byB0aGUgc3RhdGlzdGljYWwgc3VtbWFyaWVzIG92ZXJsYWlkIG9uIHRvcCwgdG8gdGhlIGF4aXMgbGFiZWxzLCBhcmUgbGF5ZXJzIGluIHRoZSBwbG90LiBUaGUgY29uc2VxdWVuY2Ugb2YgdGhpcyBpcyB0aGF0IHlvdXIgdXNlIG9mIGBnZ3Bsb3QyYCB3aWxsIHByb2JhYmx5IGludm9sdmUgaXRlcmF0aXZlIGFkZGl0aW9uIG9mIGxheWVyIHVwb24gbGF5ZXIgdW50aWwgeW91J3JlIHBsZWFzZWQgd2l0aCB0aGUgcmVzdWx0cy4KCgojIyMgQWVzdGhldGljcwoKVGhlIGdyYXBoaWNhbCBwcm9wZXJ0aWVzIHdoaWNoIGVuY29kZSB0aGUgZGF0YSB5b3UncmUgcHJlc2VudGluZyBhcmUgdGhlIF9fYWVzdGhldGljc19fIG9mIHRoZSBwbG90LiBUaGVzZSBpbmNsdWRlIHRoaW5ncyBsaWtlCgotIHggcG9zaXRpb24KLSB5IHBvc2l0aW9uCi0gc2l6ZSBvZiBlbGVtZW50cwotIHNoYXBlIG9mIGVsZW1lbnRzCi0gY29sb3Igb2YgZWxlbWVudHMKCgojIyMgR2VvbWV0cmllcwoKVGhlIHByaW1hcnkgdmlzdWFsIGl0ZW1zIG9uIHRoZSBwbG90cyBhcmUgY2FsbGVkIF9fZ2VvbWV0cmllc19fIGFuZCBpbmNsdWRlIHRoaW5ncyBsaWtlCgoqIHBvaW50cwoqIGxpbmVzCiogbGluZSBzZWdtZW50cwoqIGJhcnMKKiB0ZXh0CgpTb21lIG9mIHRoZXNlIGdlb21ldHJpZXMgaGF2ZSB0aGVpciBvd24gc3BlY2lmaWMgYWVzdGhldGljIHNldHRpbmdzLiBGb3IgZXhhbXBsZSwKCiogcG9pbnRzCiAgICAqIHBvaW50IHNoYXBlCiogdGV4dAogICAgKiB0ZXh0IGxhYmVscwoqIGxpbmVzCiAgICAqIGxpbmUgd2VpZ2h0CiAgICAqIGxpbmUgdHlwZQogIAojIyMgU3RhdGlzdGljcwoKWW91J2xsIGFsc28gZnJlcXVlbnRseSB3YW50IHRvIHBsb3QgX19zdGF0aXN0aWNzX18gb3ZlcmxhaWQgb24gdG9wIG9mLCBvciBpbnN0ZWFkIG9mIHRoZSByYXcgZGF0YS4gU29tZSBvZiB0aGVzZSBpbmNsdWRlCgoqIFNtb290aGluZyBhbmQgcmVncmVzc2lvbiBsaW5lcwoqIE9uZSBhbmQgdHdvIGRpbWVuc2lvbmFsIGJpbm5pbmcKKiBNZWFuIGFuZCBtZWRpYW5zIHdpdGggY29uZmlkZW5jZSBpbnRlcnZhbHMuCgoKLS0tLQoKVGhlIF9fYWVzdGhldGljc19fLCBfX2dlb21ldHJpZXNfXyBhbmQgX19zdGF0aXN0aWNzX18gY29uc3RpdHV0ZSB0aGUgbW9zdCBpbXBvcnRhbnQgX19sYXllcnNfXyBvZiBhIHBsb3QsIGJ1dCBmb3IgZmluZSB0dW5pbmcgYSBwbG90IGZvciBwdWJsaWNhdGlvbiwgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIG90aGVyIHRoaW5ncyB5b3UnbGwgd2FudCB0byBhZGp1c3QuIFRoZSBtb3N0IGNvbW1vbiBvbmUgb2YgdGhlc2UgYXJlIHRoZSBfX3NjYWxlc19fLCB3aGljaCBlbmNvbXBhc3MgdGhpbmdzIGxpa2UKCiogQSBsb2dhcml0aG1pYyB4IG9yIHkgYXhpcwoqIEN1c3RvbWl6ZWQgY29sb3Igc2NhbGVzCiogQ3VzdG9taXplZCBwb2ludCBzaGFwZXMsIG9yIGxpbmV0eXBlcwoKV2UnbGwgcmV2aWV3IG1hbnkgb2YgdGhlc2UgY29tcG9uZW50cyBhcyB3ZSBidWlsZCB1cCB0aGUgcGxvdCwgYW5kIHdpbGwgY2lyY2xlIGJhY2sgdG8gbW9yZSBvZiB0aGVtIGZvciBncmVhdGVyIGRldGFpbC4KCgoKIyBCdWlsZGluZyB0aGUgUGxvdAoKRmlyc3QsIGxldCdzIHJlZnJlc2ggb3VyIG1lbW9yaWVzIG9mIHRoZSBncmFwaCB3ZSB3YW50IHRvIGJ1aWxkLgoKYGBge3IgZWNobyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcud2lkdGg9OC8xLjI1LCBmaWcuaGVpZ2h0ID0gNS8xLjI1fQogZ2dwbG90KGF5X21lYW5zLCBhZXMoZG9iLCBGMV9uLCBjb2xvciA9IHBsdF92Y2xhc3MpKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHN0YXRfc21vb3RoKCkrCiAgICBzY2FsZV95X3JldmVyc2UoKSsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIiwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJ2b2ljZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZvaWNlbGVzcyIpKSsKICAgIGxhYnMoeCA9ICJEYXRlIG9mIEJpcnRoIiwKICAgICAgICAgeSA9ICJOb3JtYWxpemVkIEYxIiwKICAgICAgICAgY29sb3IgPSAiYXkvX18iKSsKICAgIGdndGl0bGUoIkNoYW5nZSBpbiAvYXkvIGFsbG9waG9uZXMiKSsKICAgIHRoZW1lX2J3KCkKYGBgCgpUaGlzIHBsb3QgaXMgY29tcG9zZWQgb2YgZWlnaHQgbGF5ZXJzLCB3aGljaCBjYW4gYmUgc3ViZGl2aWRlZCBpbnRvIGZpdmUgbGF5ZXIgdHlwZXMuIEl0J3Mgbm90IGltcG9ydGFudCBmb3IgeW91IHRvIG1lbW9yaXplIHRoZXNlIGxheWVyIHR5cGVzLCBidXQgaXQgaGVscHMgdG8gc3RydWN0dXJlIHRoZSBkaXNjdXNzaW9uLgoKIyMgTGF5ZXJzCgojIyMgVGhlIGRhdGEgbGF5ZXIKCkV2ZXJ5IGBnZ3Bsb3QyYCBwbG90IGhhcyBhIGRhdGEgbGF5ZXIsIHdoaWNoIGRlZmluZXMgdGhlIGRhdGEgc2V0IHRvIHBsb3QsIGFuZCB0aGUgYmFzaWMgbWFwcGluZ3Mgb2YgZGF0YSB0byBhZXN0aGV0aWMgZWxlbWVudHMuIFRoZSBkYXRhIGxheWVyIGNyZWF0ZWQgd2l0aCB0aGUgZnVuY3Rpb25zIGBnZ3Bsb3QoKWAgYW5kIGBhZXMoKWAsIGFuZCBsb29rcyBsaWtlIHRoaXMKCmBgYHtyIGV2YWw9Rn0KZ2dwbG90KGRhdGEsIGFlcyguLi4pKQpgYGAKClRoZSBmaXJzdCBhcmd1bWVudCB0byBgZ2dwbG90KClgIGlzIGEgZGF0YSBmcmFtZSAoaXQgX211c3RfIGJlIGEgZGF0YSBmcmFtZSksIGFuZCBpdHMgc2Vjb25kIGFyZ3VtZW50IGlzIGBhZXMoKWAuIFlvdSdyZSBuZXZlciBnb2luZyB0byB1c2UgYGFlcygpYCBpbiBhbnkgb3RoZXIgY29udGV4dCBleGNlcHQgZm9yIGluc2lkZSBvZiBvdGhlciBgZ2dwbG90MmAgZnVuY3Rpb25zLCBzbyBpdCBtaWdodCBiZSBiZXN0IG5vdCB0byB0aGluayBvZiBgYWVzKClgIGFzIGl0cyBvd24gZnVuY3Rpb24sIGJ1dCByYXRoZXIgYXMgYSBzcGVjaWFsIHdheSBvZiBkZWZpbmluZyBkYXRhLXRvLWFlc3RoZXRpYyBtYXBwaW5ncy4KCgpBbHNvIGFzIGEgcmVtaW5kZXIsIHdlJ2xsIGJlIHdvcmtpbmcgd2l0aCBhIGRhdGFmcmFtZSB0aGF0IGxvb2tzIGxpa2UgdGhpczoKCmBgYHtyfQpoZWFkKGF5X21lYW5zKQpgYGAKCldlJ2xsIHN0YXJ0IGJ5IG1hcHBpbmcgdGhlIGBkb2JgIHRvIHRoZSB4LWF4aXMsIGFuZCBgRjFfbmAgdG8gdGhlIHktYXhpcy4KCmBgYHtyfQpwIDwtIGdncGxvdChheV9tZWFucywgYWVzKHggPSBkb2IsIHkgPSBGMV9uKSkKcApgYGAKWW91IGNhbiB0aGluayBvZiB0aGlzIHBsb3QgYXMgdGhlIGJhc2UgaW1hZ2UsIGJlZm9yZSB3ZSd2ZSBhZGRlZCBhbnkgZXh0cmEgbGF5ZXJzLCB0ZXh0IG9yIGluc3RhZ3JhbSBmaWx0ZXJzIHRvIGl0LiBBbiBpbXBvcnRhbnQgY29uY2VwdHVhbCBpc3N1ZSBpcyB0aGF0IHlvdSBhcmUgYWJsZSB0byBhc3NpZ24gcGxvdHMgdG8gdmFyaWFibGVzIChpbiB0aGlzIGNhc2UsIGBwYCkuIFdoZW4geW91IGRvIHRoaXMgYXNzaWdubWVudCwgbm90aGluZyBzcGVjaWFsIGhhcHBlbnMuIEJ1dCBpZiB5b3UgcHJpbnQgb3V0IGBwYCwgUiB3aWxsIGdlbmVyYXRlIHRoZSBwbG90LiAKCgojIyMgVGhlIGdlb21ldHJpZXMgbGF5ZXIKClRoZSBuZXh0IHN0ZXAsIGFmdGVyIGRlZmluaW5nIHRoZSBiYXNpYyBkYXRhLXRvLWFlc3RoZXRpYyBtYXBwaW5ncywgaXMgdG8gYWRkIGdlb21ldHJpZXMgdG8gdGhlIGRhdGEuIFdlJ2xsIGRpc2N1c3MgZ2VvbWV0cmllcyBpbiBtb3JlIGRldGFpbCBiZWxvdywgYnV0IGZvciBub3csIHdlJ2xsIGFkZCBvbmUgb2YgdGhlIHNpbXBsZXN0OiBwb2ludHMuCgpgYGB7ciBmaWcucG9zID0gImNlbnRlciIsZmlnLndpZHRoID0gOC8xLjUsIGZpZy5oZWlnaHQ9NS8xLjV9CiAgcCA8LSBwICsgZ2VvbV9wb2ludCgpCiAgcApgYGAKClRoZXJlIGFyZSBhIGZldyB0aGluZ3MgdG8gdGFrZSBhd2F5IGZyb20gdGhpcyBzdGVwLiBGaXJzdCBhbmQgZm9yZW1vc3QsIHRoZSB3YXkgeW91IGFkZCBuZXcgbGF5ZXJzLCBvZiBhbnkga2luZCwgdG8gYSBwbG90IGlzIHdpdGggdGhlIGArYCBvcGVyYXRvci4gQW5kLCBhcyB3ZSdsbCBzZWUgaW4gYSBtb21lbnQsIHRoZXJlJ3Mgbm8gbmVlZCB0byBvbmx5IGFkZCB0aGVtIG9uZSBhdCBhIHRpbWUuIFlvdSBjYW4gc3RyaW5nIHRvZ2V0aGVyIGFueSBudW1iZXIgb2YgbGF5ZXJzIHRvIGFkZCB0byBhIHBsb3QsIHNlcGFyYXRlZCBieSBgK2AuCgpUaGUgbmV4dCB0aGluZyB0byBub3RpY2UgaXMgdGhhdCBhbGwgbGF5ZXJzIHlvdSBhZGQgdG8gYSBwbG90IGFyZSwgdGVjaG5pY2FsbHksIGZ1bmN0aW9ucy4gV2UgZGlkbid0IHBhc3MgYW55IGFyZ3VtZW50cyB0byBgZ2VvbV9wb2ludCgpYCwgc28gdGhlIHJlc3VsdGluZyBwbG90IHJlcHJlc2VudHMgdGhlIGRlZmF1bHQgYmVoYXZpb3I6IHNvbGlkIGJsYWNrIGNpcmN1bGFyIHBvaW50cy4KCklmIGZvciBubyBnb29kIHJlYXNvbiBhdCBhbGwgd2Ugd2FudGVkIHRvIHVzZSBhIGRpZmZlcmVudCBwb2ludCBzaGFwZSBpbiB0aGUgcGxvdCwgd2UgY291bGQgc3BlY2lmeSBpdCBpbnNpZGUgb2YgYGdlb21fcG9pbnQoKWAuCgpgYGB7ciBmaWcucG9zID0gImNlbnRlciIsZmlnLndpZHRoID0gOC8xLjUsIGZpZy5oZWlnaHQ9NS8xLjUsIHRpZHkgPSBGfQpnZ3Bsb3QoYXlfbWVhbnMsIGFlcyh4PWRvYiwgeT1GMV9uKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAzKQpgYGAKCk9yLCBpZiB3ZSB3YW50ZWQgdG8gdXNlIGxhcmdlciwgcmVkIHBvaW50cywgd2UgY291bGQgc3BlY2lmeSB0aGF0IGluIGBnZW9tX3BvaW50KClgIGFzIHdlbGwuCmBgYHtyIGZpZy5wb3MgPSAiY2VudGVyIixmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNSwgdGlkeSA9IEZ9CmdncGxvdChheV9tZWFucywgYWVzKHg9ZG9iLCB5PUYxX24pKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiLCBzaXplID0gMykKYGBgCgpXZSBzdGlsbCBuZWVkIHRvIGJlIHN1cmUgdG8gbWFwIHRoZSBhbGxvcGhvbmVzIHRvIHRoZSBjb2xvciBvZiB0aGUgcG9pbnRzLCB0aG91Z2guIFdlJ2xsIGRvIHRoaXMgaW4gdGhlIGRhdGEgbGF5ZXIuCgpgYGB7ciBmaWcucG9zID0gImNlbnRlciIsZmlnLndpZHRoID0gOC8xLjUsIGZpZy5oZWlnaHQ9NS8xLjV9CnAgPC0gZ2dwbG90KGF5X21lYW5zLCBhZXMoeD1kb2IsIHk9RjFfbiwgY29sb3IgPSBwbHRfdmNsYXNzKSkgKwogICAgZ2VvbV9wb2ludCgpCnAKYGBgCgoKCldlIGNhbiBzZWUgYSBmZXcgb2YgdGhlIGRlZmF1bHQgc2V0dGluZyBvZiBgZ2dwbG90MmAgb24gZGlzcGxheSBoZXJlLiBNb3N0IHN0cmlraW5nIGlzIHRoZSBsaWdodCBncmV5IGJhY2tncm91bmQsIHdpdGggd2hpdGUgZ3JpZCBsaW5lcy4gT3BpbmlvbiB2YXJpZXMgb24gd2hldGhlciBvciBub3QgdGhpcyBpcyBhZXN0aGV0aWNhbGx5IG9yIHRlY2huaWNhbGx5IHBsZWFzaW5nLCBidXQgZG9uJ3Qgd29ycnksIGl0J3MgYWRqdXN0YWJsZS4gCgpBbm90aGVyIGRlZmF1bHQgaXMgdG8gbGFiZWwgdGhlIHggYW5kIHkgYXhlcyB3aXRoIHRoZSBjb2x1bW4gbmFtZXMgZnJvbSB0aGUgZGF0YSBmcmFtZS4gSSdsbCBpbmplY3QgYSBiaXQgb2YgYmVzdCBwcmFjdGljZSBhZHZpY2UgaGVyZSwgYW5kIHRlbGwgeW91IHRvIF9hbHdheXNfIGNoYW5nZSB0aGUgYXhpcyBuYW1lcy4gSXQncyBuZWFybHkgZ3VhcmFudGVlZCB0aGF0IHlvdXIgZGF0YSBmcmFtZSBjb2x1bW4gbmFtZXMgd2lsbCBtYWtlIGZvciB2ZXJ5IHBvb3IgYXhpcyBsYWJlbHMuIFdlJ2xsIGNvdmVyIGhvdyB0byBkbyB0aGF0IHNob3J0bHkuCgpGaW5hbGx5LCBub3RlIHRoYXQgd2UgZGlkbid0IG5lZWQgdG8gdGVsbCBgZ2VvbV9wb2ludCgpYCBhYm91dCB0aGUgeCBhbmQgeSBheGVzLiBUaGlzIG1heSBzZWVtIHRyaXZpYWwsIGJ1dCBpdCdzIGEgcmVhbGx5IGltcG9ydGFudCwgYW5kIHBvd2VyZnVsIGFzcGVjdCBvZiBgZ2dwbG90MmAuIFdoZW4geW91IGFkZCBhbnkgbGF5ZXIgYXQgYWxsIHRvIGEgcGxvdCwgaXQgd2lsbCBfX2luaGVyaXRfXyB0aGUgZGF0YS10by1hZXN0aGV0aWMgbWFwcGluZ3Mgd2hpY2ggd2VyZSBkZWZpbmVkIGluIHRoZSBkYXRhIGxheWVyLiBXZSdsbCBkaXNjdXNzIGluaGVyaXRhbmNlLCBhbmQgaG93IHRvIG92ZXJyaWRlLCBvciBkZWZpbmUgbmV3IGRhdGEtdG8tYWVzdGhldGljIG1hcHBpbmdzIHdpdGhpbiBhbnkgZ2VvbS4KCgojIyMgVGhlIHN0YXRpc3RpY3MgbGF5ZXIKClRoZSBmaW5hbCBmaWd1cmUgYWxzbyBpbmNsdWRlcyBhIHNtb290aGluZyBsaW5lLCB3aGljaCBpcyBvbmUgb2YgbWFueSBwb3NzaWJsZSBzdGF0aXN0aWNhbCBsYXllcnMgd2UgY2FuIGFkZCB0byBhIHBsb3QuCmBgYHtyIGZpZy53aWR0aCA9IDgvMS41LCBmaWcuaGVpZ2h0PTUvMS41fQogIHAgPC0gcCArIHN0YXRfc21vb3RoKCkKICBwCmBgYAoKV2UnbGwgZ28gb3ZlciB0aGUgZGVmYXVsdCBiZWhhdmlvciBvZiBgc3RhdF9zbW9vdGgoKWAgYmVsb3csIGJ1dCBpbiB0aGlzIHBsb3QsIHRoZSBzbW9vdGhpbmcgbGluZSByZXByZXNlbnRzIGEgbG9lc3Mgc21vb3RoLCBhbmQgdGhlIHNlbWktdHJhbnNwYXJlbnQgcmliYm9uIHN1cnJvdW5kaW5nIHRoZSBzb2xpZCBsaW5lIGlzIHRoZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbC4KCk9uZSBpbXBvcnRhbnQgdGhpbmcgdG8gcmVhbGl6ZSBpcyB0aGF0IGl0J3Mgbm90IG5lY2Vzc2FyeSB0byBpbmNsdWRlIHRoZSBwb2ludHMgaW4gb3JkZXIgdG8gYWRkIGEgc21vb3RoaW5nIGxpbmUuIEhlcmUncyB3aGF0IHRoZSBwbG90IHdvdWxkIGxvb2sgbGlrZSB3aXRoIHRoZSBwb2ludHMgb21pdHRlZC4KCmBgYHtyIGZpZy53aWR0aCA9IDgvMS41LCBmaWcuaGVpZ2h0PTUvMS41LCB0aWR5ID1GfQogZ2dwbG90KGF5X21lYW5zLCBhZXMoeD1kb2IsIHk9RjFfbiwgY29sb3IgPSBwbHRfdmNsYXNzKSkgKwogIHN0YXRfc21vb3RoKCkKYGBgCgoKTm90aWNlIGhvdyB0aGUgeS1heGlzIGhhcyB6b29tZWQgaW4gdG8ganVzdCBpbmNsdWRlIHRoZSByYW5nZSBvZiB0aGUgc21vb3RoaW5nIGxpbmUgYW5kIHN0YW5kYXJkIGVycm9yLgoKIyMjIFNjYWxlIHRyYW5zZm9ybWF0aW9ucwpJIGFsc28gd2FudGVkIHRvIG1ha2Ugc29tZSBhbHRlcmF0aW9ucyB0byB0aGUgZGVmYXVsdCB5IGF4aXMgc2NhbGVzLiBUaGUgeS1heGlzIGlzIGN1cnJlbnRseSBydW5uaW5nIGluIHJldmVyc2UgdG8gdGhlIGludHVpdGl2ZSBkaXJlY3Rpb24gb2YgRjEuIF9IaWdoZXJfIHZvd2VscyBoYXZlIF9sb3dlcl8gRjEgdmFsdWVzLCBzbyB3ZSB3YW50IHRvIGZsaXAgdGhlIHktYXhpcy4gSSBhbHNvIHdhbnQgdG8gY2hhbmdlIHRoZSBjb2xvciBzY2FsZSwgYW5kIGl0cyBsYWJlbHMuCgoKYGBge3IgdGlkeSA9IEYsZmlnLndpZHRoID0gOC8xLjUsIGZpZy5oZWlnaHQ9NS8xLjV9CnAgPC0gcCArIHNjYWxlX3lfcmV2ZXJzZSgpKwogICAgICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygidm9pY2VkIiwidm9pY2VsZXNzIikpCnAKYGBgCgoKSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgc21vb3RoaW5nIGxpbmUgaGVyZSBpcyBjYWxjdWxhdGVkIG92ZXIgdGhlIF90cmFuc2Zvcm1lZF8gZGF0YS4KCgpUaGUgb3RoZXIga2luZCBvZiBzY2FsZSB0cmFuc2Zvcm1hdGlvbiB5b3UncmUgbW9zdCBsaWtlbHkgdG8gbWFrZSB3b3VsZCBiZSB1c2UgYSBsb2cgc2NhbGUgb24gZGF0YSBsaWtlIGR1cmF0aW9uczoKCgo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDAlO2Zsb2F0OmxlZnQ7Ij4KPGRpdiBzdHlsZSA9ICJ3aWR0aDozNSU7ZmxvYXQ6bGVmdDttYXJnaW4tbGVmdDoxMCU7bWFyZ2luLXJpZ2h0OjUlO21hcmdpbi1ib3R0b206NSU7Ij4KCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDgvMywgZmlnLmhlaWdodD01LzN9CmdncGxvdChheSwgYWVzKGR1cikpKwogICAgc3RhdF9iaW4oKQpgYGAKCjwvZGl2PgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDozNSU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87bWFyZ2luLXJpZ2h0OjEwJTttYXJnaW4tbGVmdDo1JTttYXJnaW4tYm90dG9tOjUlOyI+CgpgYGB7ciB0aWR5ID0gRixmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNX0KZ2dwbG90KGF5LCBhZXMoZHVyKSkrCiAgICBzdGF0X2JpbigpKwogICAgc2NhbGVfeF9sb2cxMCgpCmBgYAoKPC9kaXY+CgoKPC9kaXY+CgoKIyMjIENvc21ldGljIGFsdGVyYXRpb25zCkZpbmFsbHksIEkgd2FudGVkIHRvIG1ha2Ugc29tZSBjb3NtZXRpYyBhZGp1c3RtZW50cyB0byB0aGUgcGxvdC4gRm9yIGV4YW1wbGUsIHRoZSBheGlzIGxhYmVscyBhbGwgbmVlZCB0byBiZSByZW5hbWVkLiBJIGFsc28gYWRkZWQgYSB0aXRsZSB0byB0aGUgcGxvdCwgYW5kIGNoYW5nZWQgdGhlIGNvbG9yIHRoZW1lIHRvIGJsYWNrIGFuZCB3aGl0ZS4KCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDgvMS41LCBmaWcuaGVpZ2h0PTUvMS41fQpwIDwtIHAgKyBsYWJzKHggPSAiRGF0ZSBvZiBCaXJ0aCIsCiAgICAgICAgICAgICAgeSA9ICJOb3JtYWxpemVkIEYxIiwKICAgICAgICAgICAgICBjb2xvciA9ICJheS9fIikrCiAgICAgICAgIHRoZW1lX2J3KCkrCiAgICAgICAgZ2d0aXRsZSgiQ2hhbmdlIGluIC9heS8gYWxsb3Bob25lcyIpCgpwCmBgYAoKCkhlcmUncyBob3cgYSBzaW1pbGFyIHZlcnNpb24gb2YgdGhpcyBwbG90IGxvb2tzIGluIHByaW50CgoKIVtdKGZpZ3VyZXMvYXlzX21lYW5zX3Bsb3QtMS5wbmcpCgoKCiMgRnV0aGVyIHJlYWRpbmcgYW5kIGV4cGxvcmF0aW9uCgpGb3IgZnVydGhlciByZWFkaW5nIG9uIGhvdyB0byB1c2UgZ2dwbG90Miwgc3BlY2lmaWNhbGx5LCBJJ2QgaGlnaGx5IHJlY29tbWVuZCBLaWVyYW4gSGVhbHkncyBuZXcgYm9vayAqRGF0YSBWaXN1YWxpemF0aW9uOiBBIFByYWN0aWNhbCBJbnRyb2R1Y3Rpb24qLgoKPGRpdiBjbGFzcyA9ICJoYWxmLWltZyI+CgpbIVtdKGZpZ3VyZXMvZHZfY292ZXJfdGlueS5wbmcpXShodHRwczovL3d3dy5hbWF6b24uY29tL2dwL3Byb2R1Y3QvMDY5MTE4MTYyNC9yZWY9YXNfbGlfdGw/aWU9VVRGOCZ0YWc9a2llcmFuaGVhbHlzdy0yMCZjYW1wPTE3ODkmY3JlYXRpdmU9OTMyNSZsaW5rQ29kZT1hczImY3JlYXRpdmVBU0lOPTA2OTExODE2MjQmbGlua0lkPTE2ZDUzYjNjYzFlYzNiYzNhYWM2MGIyN2MyOWI5MmU4KQoKPC9kaXY+CgpJJ2QgYWxzbyBzdWdnZXN0IGNoZWNraW5nIG91dDoKCgotIFRoZSBbZ2d0aGVtZXMgcGFja2FnZV0oaHR0cHM6Ly93d3cuZ2dwbG90Mi1leHRzLm9yZy9nZ3RoZW1lcy5odG1sKQotIFRoZSBbZ2dyZXBlbCBwYWNrYWdlXShodHRwczovL3d3dy5nZ3Bsb3QyLWV4dHMub3JnL2dncmVwZWwuaHRtbCkKLSBUaGUgW2dnZm9yY2UgcGFja2FnZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dnZm9yY2UvdmlnbmV0dGVzL1Zpc3VhbF9HdWlkZS5odG1sKQotIFRoaXMgW3Bvc3Qgb24gdGhlIHZpcmlkaXMgc2NhbGVdKGh0dHBzOi8vcnRhc2sudGhpbmtyLmZyL2Jsb2cvZ2dwbG90Mi13ZWxjb21lLXZpcmlkaXMvKQoKRm9yIG1vcmUgZ2VuZXJhbCBkYXRhIHZpc3VhbGl6YXRpb24gcmVhZGluZywgeW91IHJlYWxseSBuZWVkIHRvIGF0IGxlYXN0IGhhdmUgYW4gb3BpbmlvbiBhYm91dCBFZHdhcmQgVHVmdGUncyAqVmlzdWFsIERpc3BsYXkgb2YgUXVhbnRpdGF0aXZlIEluZm9ybWF0aW9uKgoKPGRpdiBjbGFzcyA9ICJoYWxmLWltZyI+ClshW10oZmlndXJlcy92aXNfZGlzcGxheS5wbmcpXShodHRwczovL3d3dy5lZHdhcmR0dWZ0ZS5jb20vdHVmdGUvYm9va3NfdmRxaSkKPC9kaXY+CgoKCg==